The .NET client library logs requests, responses, and summary messages made to
the Google Ads API. The logs can be written to a custom
[`TraceListener`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracelistener) or to a custom [`ILogger`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger)
instance.

> [!CAUTION]
> **Caution:** Although the client library redacts OAuth tokens in logs by default, log files can still contain sensitive customer and campaign information. Always restrict file permissions on directories where log files are stored.

## TraceListener

You can enable logging to a `TraceListener` by adding the following lines in
your `Main` method before making any API calls:

    using Google.Ads.GoogleAds.Util;

    // Detailed logs.
    TraceUtilities.Configure(
        TraceUtilities.DETAILED_REQUEST_LOGS_SOURCE,
        "logs/details.log",
        System.Diagnostics.SourceLevels.All);

    // Summary logs.
    TraceUtilities.Configure(
        TraceUtilities.SUMMARY_REQUEST_LOGS_SOURCE,
        "logs/summary.log",
        System.Diagnostics.SourceLevels.All);

> [!NOTE]
> **Note:** Call `TraceUtilities.Configure` only once per application lifecycle. The `TraceListener` it creates is automatically reused across all API requests made by the application; you don't need to create a new one for each API request.

## ILogger

If you're already using an `ILogger` for your application logs, this solution
lets you integrate Google Ads API logs into your existing logging infrastructure.

First, create a `LoggerFactory`, or if you already have one, add the filters
for Google Ads API logs:

    var loggerFactory = LoggerFactory.Create(builder =>
    {
        // Log to stdout.
        builder.AddConsole();
        builder.AddFilter(
            TraceUtilities.SUMMARY_REQUEST_LOGS_SOURCE, LogLevel.Trace);
        builder.AddFilter(
            TraceUtilities.DETAILED_REQUEST_LOGS_SOURCE, LogLevel.Trace);
    });

Then, use the `LoggerFactory` to create loggers for request and response
summaries and details:

    ILogger summaryLogger = loggerFactory.CreateLogger(
        TraceUtilities.SUMMARY_REQUEST_LOGS_SOURCE);
    ILogger detailLogger = loggerFactory.CreateLogger(
        TraceUtilities.DETAILED_REQUEST_LOGS_SOURCE);

Finally, configure the client library to redirect its traces to your `ILogger`
instances:

    TraceUtilities.ConfigureSummaryLogger(summaryLogger);
    TraceUtilities.ConfigureDetailLogger(detailLogger);

This solution lets you integrate Google Ads API request and response logs into
existing logging frameworks, such as Log4Net, NLog, and Serilog.

## Log levels

The library logs different types of events at different log levels:

- **Successful API response** : Summary is logged at `INFO`; full request and response are logged at `DEBUG`.
- **API error response** : Summary message is logged at `WARN`; full request and response are logged at `INFO`.
- **Partial failures** : Logged at `DEBUG`.

## Request ID

In most cases, the logs generated by the client library provide sufficient
details to troubleshoot your issues. When you reach out to
[support](https://developers.google.com/google-ads/api/support/contact), either provide the logs (which
redact sensitive authentication fields by default) or share the request ID,
which is logged as part of the response log.

If you prefer capturing the request ID programmatically, use one of the
following approaches.

### Extract from ordinary unary API calls

You can use a custom `CallSettings` with a `TrailingMetadataHandler` to capture
request IDs from regular unary calls:

    CallSettings callSettings = CallSettings.FromTrailingMetadataHandler(
        metadata =>
        {
            // Extract and log the request ID from the trailing metadata.
            string requestId = metadata.Get("request-id")?.Value;
            Console.WriteLine($"Request ID: {requestId}");
        });
    // Add the campaigns.
    MutateCampaignsResponse retVal = campaignService.MutateCampaigns(
        customerId.ToString(), operations.ToArray(), callSettings);

### Extract from streaming API calls

The request ID is returned as part of the response object for streaming API
calls. For example, you can get the request ID for a `SearchStream` call as
follows:

    // Get the GoogleAdsService.
    GoogleAdsServiceClient googleAdsService = client.GetService(
        Services.V26.GoogleAdsService);

    // Retrieve all campaigns.
    string query = @"SELECT
                    campaign.id,
                    campaign.name,
                    campaign.network_settings.target_content_network
                FROM campaign
                ORDER BY campaign.id";

    // Issue a search request.
    googleAdsService.SearchStream(
        customerId.ToString(),
        query,
        (SearchGoogleAdsStreamResponse resp) =>
        {
            // Extract the request ID from the response.
            string requestId = resp.RequestId;
            Console.WriteLine($"Request ID: {requestId}");
            foreach (GoogleAdsRow googleAdsRow in resp.Results)
            {
                Console.WriteLine(
                    "Campaign with ID {0} and name '{1}' was found.",
                    googleAdsRow.Campaign.Id,
                    googleAdsRow.Campaign.Name);
            }
        }
    );

### Exceptions

The request ID is returned as part of the `GoogleAdsException` exception
whenever an API call fails:

    try
    {
        // Make an API call.
    }
    catch (GoogleAdsException e)
    {
        string requestId = e.RequestId;
        Console.WriteLine($"Failed Request ID: {requestId}");
    }

## Configure advanced logging

If the API log doesn't give you enough details, enable lower-level logging at
the gRPC transport level. Keep in mind that the output can be voluminous.

When using the default `Grpc.Net.Client` transport, configure transport-level
logs through .NET's [`ILogger`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger) provider by adding a filter for
the `Grpc` category (for example, `builder.AddFilter("Grpc", LogLevel.Debug)`).

If you are using the legacy `Grpc.Core` transport (`UseGrpcCore = true`), gRPC
logs are written to `stderr` by default, or you can attach your own logger as
shown in the following example. For more details, see the
[supported gRPC environment variables](https://github.com/grpc/grpc/blob/master/doc/environment_variables.md).

    Environment.SetEnvironmentVariable("GRPC_VERBOSITY", "DEBUG");
    Environment.SetEnvironmentVariable("GRPC_TRACE", "http");
    GrpcEnvironment.SetLogger(new ConsoleLogger());

## TraceListener configuration using App.config (legacy)

If your app builds for a .NET Framework target, you can load the logging
configuration from your app's `App.config` or `Web.config` file. This is a
legacy .NET functionality that is not supported for apps built for modern .NET
targets.

To use this feature, add the following changes to your configuration file:

1. Add the following snippet under the `<configuration>` section:

       <system.diagnostics>
         <sources>
           <source name="GoogleAds.DeprecationMessages"
               switchName="GoogleAds.DeprecationMessages"
               switchType="System.Diagnostics.SourceSwitch">
             <listeners>
               <add name="myListener"
                    type="System.Diagnostics.EventLogTraceListener"
                    initializeData="Application" />
             </listeners>
           </source>
           <source name="GoogleAds.DetailedRequestLogs"
               switchName="GoogleAds.DetailedRequestLogs"
               switchType="System.Diagnostics.SourceSwitch">
             <listeners>
               <add name="detailedRequestLogListener"
                    type="System.Diagnostics.ConsoleTraceListener"
                    initializeData="true" />
               <!-- Use the following to log to a file. Modify the initializeData
                    attribute to control the path to the detailed request log
                    file. -->
               <!--
               <add name="detailedRequestLogListener"
                    type="System.Diagnostics.TextWriterTraceListener"
                    initializeData="C:\Logs\detailed_logs.log" />
               <remove name="Default" />
               -->
             </listeners>
           </source>
           <source name="GoogleAds.SummaryRequestLogs"
               switchName="GoogleAds.SummaryRequestLogs"
               switchType="System.Diagnostics.SourceSwitch">
             <listeners>
               <add name="summaryRequestLogListener"
                    type="System.Diagnostics.ConsoleTraceListener"
                    initializeData="true" />
               <!-- Use the following to log to a file. Modify the initializeData
                    attribute to control the path to the summary request log
                    file. -->
               <!--
               <add name="summaryRequestLogListener"
                    type="System.Diagnostics.TextWriterTraceListener"
                    initializeData="C:\Logs\summary_logs.log" />
               -->
               <remove name="Default" />
             </listeners>
           </source>
         </sources>
         <switches>
           <!-- Use this trace switch to control the deprecation trace messages
                written by Google Ads .NET libraries. The default level is set to
                Warning. To disable all messages, set this value to Off. -->
           <add name="GoogleAds.DeprecationMessages" value="Warning" />
           <!-- Use this trace switch to control the detailed request logs written
                by Google Ads .NET libraries. The default level is set to Off.
                Logs are generated at both the Error and Information levels. -->
           <add name="GoogleAds.DetailedRequestLogs" value="Off" />
           <!-- Use this trace switch to control the summary request logs written
                by Google Ads .NET libraries. The default level is set to Off.
                Logs are generated at both the Error and Information levels. -->
           <add name="GoogleAds.SummaryRequestLogs" value="Off" />
         </switches>
         <trace autoflush="true" />
       </system.diagnostics>

2. Add the following snippet under the `<configSections>` section:

       <section name="system.diagnostics"
                type="System.Diagnostics.SystemDiagnosticsSection" />

   Your `App.config` then looks like this:

       <?xml version="1.0" encoding="utf-8"?>
       <configuration>
         <configSections>
           <section name="GoogleAdsApi"
                    type="System.Configuration.DictionarySectionHandler" />
           <section name="system.diagnostics"
                    type="System.Diagnostics.SystemDiagnosticsSection" />
         </configSections>
         <GoogleAdsApi>
           <!-- Google Ads API settings. -->
         </GoogleAdsApi>
         <system.diagnostics>
           <!-- Logging settings. -->
         </system.diagnostics>
       </configuration>