Logging Configuration
- Vinaya Mayya
The as-shipped Ed-Fi ODS / API uses the Apache log4net framework to assist developers and operations staff log system output at a variety of output levels and to a variety of output targets. The log4net documentation provides an overview of the framework capabilities and options. The documentation is excellent, so we won't repeat the basics here.
Logging settings for the ODS / API itself and the ODS / API Administration portal can be configured separately. This page provides example logging configurations for specific scenarios.
Default Release Configuration
This configuration is found in the as-shipped ODS / API source. This configuration routes all log messages at the WARN level or above to a rolling file log. This will log to a single file with up to two backup files, and the space taken by each file will not exceed 20MB. This represents the default configuration for a release build.
<log4net> <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <threshold value="WARN" /> <file value="WebApiLog.log" /> <appendToFile value="true" /> <maximumFileSize value="20MB" /> <maxSizeRollBackups value="7" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread,%property{ApiClientId},%property{CorrelationId}] %-5level %logger - MESSAGE: %message%newline %exception" /> </layout> </appender> <logger name="NHibernate"> <level value="WARN"/> </logger> <root> <appender-ref ref="RollingFileAppender" /> </root> </log4net>
Default Local Developer Configuration
This configuration routes all logging messages at the INFO level or above to the Trace output (i.e., Console), and also provides a file appender for a stable log file to review. In addition, this configuration provides a separate Request-Response log file, which logs ERROR level request-response details containing ApiClientId, Request Url, Request Method, Response Code, and Response Message for all Resource endpoints. This represents the default configuration for a debug build.
<?xml version="1.0" encoding="utf-8" ?> <log4net> <appender name="TraceAppender" type="log4net.Appender.TraceAppender"> <threshold value="INFO" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread,%property{ApiClientId},%property{CorrelationId}] %-5level %logger{1}.%method [%line] - MESSAGE: %message%newline %exception" /> </layout> </appender> <appender name="RollingFile" type="log4net.Appender.RollingFileAppender"> <threshold value="WARN" /> <file value="WebApiLog.log" /> <appendToFile value="true" /> <maximumFileSize value="100KB" /> <maxSizeRollBackups value="7" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread,%property{ApiClientId},%property{CorrelationId}] %-5level %logger - MESSAGE: %message%newline %exception" /> </layout> </appender> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <threshold value="INFO" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread,%property{ApiClientId},%property{CorrelationId}] %-5level %logger{1} - %message%newline" /> </layout> </appender> <appender name="AppInsightsAppender" type="Microsoft.ApplicationInsights.Log4NetAppender.ApplicationInsightsAppender, Microsoft.ApplicationInsights.Log4NetAppender"> <threshold value="WARN" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%message%newline" /> </layout> </appender> <logger name="NHibernate"> <level value="WARN"/> </logger> <root> <appender-ref ref="ConsoleAppender" /> <appender-ref ref="RollingFile" /> <appender-ref ref="TraceAppender" /> <appender-ref ref="AppInsightsAppender" /> </root> <logger name="RequestResponseDetailsLogger" additivity="false"> <level value="ERROR" /> <appender-ref ref="RequestResponseDetailsFileAppender" /> </logger> <appender name="RequestResponseDetailsFileAppender" type="log4net.Appender.RollingFileAppender"> <threshold value="ERROR" /> <file value="WebApiRequestResponseDetailsLog.log"/> <appendToFile value="true"/> <maximumFileSize value="100KB" /> <maxSizeRollBackups value="7" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level ClientId:%property{ApiClientId} CorrelationId:%property{CorrelationId} RequestUrl:%property{RequestUrl} RequestMethod:%property{RequestMethod} ResponseCode:%property{ResponseCode} ResponseMessage:%message%n" /> </layout> </appender> </log4net>
To disable Request-Response logs, remove both logger RequestResponseDetailsLogger and appender RequestResponseDetailsFileAppender from the configuration file.
Example Support Configuration
In production, it's often useful to log more activity such as when troubleshooting a difficult-to-reproduce issue. This example configuration routes all logging messages at the DEBUG level or above (i.e., all logging messages) to a rolling file log. This will log a large amount of information and should not be left on after reproducing the issue for the support ticket. It increases the rolling size-per-file value to 100MB to ensure all the data is collected, but reduces the number of backup files to one single file. After enabling this configuration, detailed files can be inspected to aid in troubleshooting.
<log4net> <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <threshold value="DEBUG" /> <file value="WebApiLog.log" /> <appendToFile value="true" /> <maximumFileSize value="100MB" /> <maxSizeRollBackups value="7" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread,%property{ApiClientId},%property{CorrelationId}] %-5level %logger - MESSAGE: %message%newline %exception" /> </layout> </appender> <logger name="NHibernate"> <level value="WARN"/> </logger> <root> <appender-ref ref="RollingFileAppender" /> </root> </log4net>
To further aid production troubleshooting, RequestResponseDetailsLogger can be configured to log request/response JSON to the ODS associated with the API request. See How To: Enable logging API request and response content for details.
Correlation ID
The Correlation ID is a unique identifier associated with each API request's lifecycle. Log entries and API error responses related to a specific client request will include the Correlation ID. The Correlation ID provides a means of identifying log entries associated with the lifecycle of a particular request.
Example 1: API error response body containing a Correlation ID
{ "detail": "Identifying values for the Calendar data cannot be changed. Delete and recreate the item instead.", "type": "urn:ed-fi:api:bad-request:data-validation-failed:key-change-not-supported", "title": "Key Change Not Supported", "status": 400, "correlationId": "b9567d36-91c1-4bae-aff5-3fde8367b969" }
Example 2: a log entry created during the same request used for Example 1 demonstrates that it contains the same Correlation ID.
2024-05-29 11:40:00,268 [.NET TP Worker,2,b9567d36-91c1-4bae-aff5-3fde8367b969] ERROR EdFi.Ods.Api.Services.Controllers.Calendars.EdFi.CalendarsController - MESSAGE: Put EdFi.Ods.Common.Exceptions.KeyChangeNotSupportedException: Identifying values for the Calendar data cannot be changed. Delete and recreate the item instead.
API clients (or infrastructure capable of modifying the HTTP request in transit to the API) can optionally specify the Correlation ID for each request. If a request is received which does not specify a Correlation ID, the ODS will generate a GUID to serve as the correlation ID for that remainder of the request's lifecycle.
Specifying the Correlation ID for a Request
API requests can optionally provide their own Correlation ID value in one of two ways:
- Including it in a query string parameter named 'correlationId'
- Including it as an HTTP header named 'correlation-id'
When the Correlation ID is specified in API requests, it must be ensured that the Correlation ID used for each request is unique. Creating a GUID to serve as the Correlation ID for each request is a straightforward and reliable approach.
Although setting a Correlation ID value in both the query string and HTTP header on the same request is never necessary, the ODS will use the value from the HTTP header if that situation arises.
To maximize the utility of the Correlation ID, it should be added to an API request as early in its lifecycle as possible. This allows other infrastructure or services between the API client and ODS instance the opportunity to incorporate the Correlation ID into their logging.
Example 3: A query string parameter is used to specify the Correlation ID
GET /data/v3/ed-fi/schools?offset=0&limit=1&totalCount=false?correlationId=b9567d36-91c1-4bae-aff5-3fde8367b969 HTTP/1.1 Host: localhost:54746 Accept: application/json Use-Snapshot: false Authorization: Bearer 7965b8a43edb4c9fa22c4529e96b0ec8
Example 4: An HTTP header is used to specify the Correlation ID
GET /data/v3/ed-fi/schools?offset=0&limit=1&totalCount=false HTTP/1.1 Host: localhost:54746 Accept: application/json Use-Snapshot: false Authorization: Bearer 7965b8a43edb4c9fa22c4529e96b0ec8 correlation-id: b9567d36-91c1-4bae-aff5-3fde8367b969
Developers' Guide Contents
Find out more about how to develop platforms based on the Ed-Fi ODS / API v7.1: