Connecting log data to traces
powered by Grail
Dynatrace can enrich your ingested log data with additional information that helps Dynatrace to recognize, correlate, and evaluate the data. Log enrichment results in a more refined analysis of your logs.
OneAgent version 1.239+
Automatically connecting log data to traces works for all log data, no matter how the log data was ingested by Dynatrace.
You can manually enrich logs for log data ingested by Dynatrace by defining a log pattern to include the dt.span_id
, dt.trace_id
, dt.trace_sampled
and dt.entity.process_group_instance
fields.
Log enrichment enables you to:
Seamlessly switch context and analyze individual spans, transactions, or entire workloads
Empower development teams by making it easier and faster for them to detect and pinpoint problems
Supported frameworks
Supported frameworks for trace/span log context enrichment:
.NET
Logging frameworks | Versions |
---|---|
Microsoft Logging Extensions | 3.0.0+ |
Serilog | 2.9+ |
log4net | 2.0.6+ |
Apache HTTP Server
Automatic log enrichment is supported for error.logs
and access.logs
Go
Logging frameworks | Versions | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Logrus | 1.7.1 - 1.91 | ||||||||||
Zap | 1.10 - 1.26 | ||||||||||
1 Versions 1.7.0 and lower are not supported due to a race condition problem in the Logrus framework |
Java
Logging frameworks | Versions |
---|---|
JBoss LogManager | 1.1+, 2.x, 3.x |
Log4J2 (Apache) | 2.7 - 2.12, 2.13.0, 2.13.1, 2.13.3, 2.14 - 2.17.1, 2.17.2 - 2.22 |
Logback (QOS) | 1 |
java.util.logging | All versions supported |
NGINX
Automatic log enrichment is supported for error.logs
, but manual log enrichment is required for access.logs
Node.js
PHP
Logging frameworks | Versions |
---|---|
Monolog | 2.3 - 2.4, 3.0 |
Supported frameworks for trace/span unstructured log context enrichment:
.NET
Java
There are two ways to enrich the log data that you send to Dynatrace:
- Automatic log enrichment
This method is recommended for common technologies and applications generating structured log data. - Manual log enrichment
This method is recommended for custom technologies and applications generating unstructured log data.
Enrich logs automatically
You can enable log enrichment for a particular technology used to create log data and let Dynatrace automatically inject additional attributes into every log record received. This method is recommended for structured log data of known technologies.
Use Process group override to limit log enrichment to a specific process group or a process within a process group.
Enable/disable log enrichment for a specific technology
To enable log enrichment for a specific technology:
What does automatic log enrichment do?
Log enrichment modifies your ingested log data and adds the following information to each detected log record:
dt.trace_id
dt.span_id
dt.entity.process_group_instance
Structured log data
For structured log data such as JSON, XML, and well-defined text log formats, Dynatrace adds an attribute field to the log record entry.
Example of enriched log data in JSON format
Log data in JSON format is enriched with additional <dt.trace_id>
, <dt.span_id>
, and dt.entity.process_group_instance
properties.
1{2 "severity": "error",3 "time": 1638957438023,4 "pid": 1,5 "hostname": "paymentservice-788946fdcd-42lgq",6 "name": "paymentservice-charge",7 "dt.trace_id": "d04b42bc9f4b6ecdbf6bc9f4b6ecdbc",8 "dt.span_id": "9adc716eb808d428",9 "dt.entity.process_group_instance": "PROCESS_GROUP_INSTANCE-27204EFED3D8466E",10 "message": "Unsupported card type for cardNumber=************0454"11}
Example of enriched log data in XML format
Log data in XML format is enriched with additional <dt.trace_id>
, <dt.span_id>
, and <dt.entity.process_group_instance>
nodes.
1<?xml version="1.0" encoding="windows-1252" standalone="no"?>2<record>3 <date>2021-08-24T14:41:36.565218700Z</date>4 <millis>1629816096565</millis>5 <nanos>218700</nanos>6 <sequence>0</sequence>7 <logger>com.apm.testapp.logging.jul.XMLLoggingSample</logger>8 <level>INFO</level>9 <class>com.apm.testapp.logging.jul.BaseLoggingSample</class>10 <method>info</method>11 <thread>1</thread>12 <message>Update completed successfully.</message>13 <dt.trace_id>513fcd4e9b08792fcd4e9b08792</dt.trace_id>14 <dt.span_id>125840e3125840e3</dt.span_id>15 <dt.entity.process_group_instance>PROCESS_GROUP_INSTANCE-27204EFED3D8466E</dt.entity.process_group_instance>16</record>
Unstructured log data
Check if Dynatrace log enrichment has an impact on your existing log data pipeline before using automatic log enrichment on unstructured log data.
Unstructured log data is typically made of raw plain text that is sequentially ordered and is designed to be read by people. Dynatrace does not automatically enrich unstructured log data. Dynatrace is able to enrich unstructured log data, but appending additional information to log data may have an impact on third-party tools that consume that same log data.
Example of enriched log data in raw text format
Log data in raw text is enriched with an additional [!dt dt.trace_id=$trace_id, dt.span_id=$span_id, dt.entity.process_group_instance=$dt.entity.process_group_instance]
string (attributes and their value).
1127.0.0.1 - [21/Oct/2021:10:33:28 +0200] GET /index.htm HTTP/1.1 404 597 [!dt dt.trace_id=aa764ee37ebaa764ee37eaa764ee37e,dt.span_id=b93ede8b93ede8, dt.entity.process_group_instance=PROCESS_GROUP_INSTANCE-27204EFED3D8466E]
Enrich logs manually
OneAgent version 1.239+
You can manually enrich your Dynatrace ingested log data by defining a log pattern to include the dt.span_id
, dt.trace_id
, dt.trace_sampled
, and dt.entity.process_group_instance fields
. You can enable manual log enrichment for a specific technology by following the Log enrichment steps.
Be sure to follow these rules for the format of the enriched fields in an unstructured log:
- Fields must be encapsulated in square brackets (
[]
) with a!dt
prefix.
For example,[!dt dt.trace_id=$dt_trace_id,dt.span_id=$dt_span_id, dt.entity.process_group_instance=$dt.entity.process_group_instance]
Fields must be formatted without double quotes.
Any invalid characters for the field and field value must be escaped.
- Any control characters like
\n
must be excluded from the enrichment definition.
Example of manually enriching NGINX log data
Suppose you want to manually enrich your NGINX log data with dt.trace_id
, dt.span_id
and dt.trace_sampled
. The NGINX configuration file contains numerous standard NGINX variables, your log format definition must be in the log_format
section. For example:
1log_format custom '$remote_addr - [$time_local] $request $status $body_bytes_sent [!dt dt.trace_id=$dt_trace_id,dt.span_id=$dt_span_id,dt.trace_sampled=$dt_trace_sampled]';2access_log logs/access.log custom;
The result will be an access.log
file containing the enriched log records:
1127.0.0.1 - [22/Mar/2022:08:50:45 +0100] GET /index.htm HTTP/1.1 200 30 [!dt dt.trace_id=b9e5c9ec08be5fab5071d76f427be7da,dt.span_id=43c5bb9432593963,dt.trace_sampled=true]2127.0.0.1 - [22/Mar/2022:08:50:45 +0100] GET /index.htm HTTP/1.1 200 30 [!dt dt.trace_id=01e52950b145d97bf22345e68c5e6c58,dt.span_id=de819d856eecb236,dt.trace_sampled=true]
For OneAgent version 1.237 and earlier, the NGINX variables used are different. For example:
1log_format custom '$remote_addr - [$time_local] $request $status $body_bytes_sent [!dt dt.trace_id=$trace_id,dt.span_id=$span_id]'; access_log logs/access.log custom
The result will be an access.log
file containing the enriched log records:
1127.0.0.1 - [21/Oct/2021:10:33:28 +0200] GET /index.htm HTTP/1.1 404 597 [!dt dt.trace_id=e1c0afeb0b8a91d7748139aa764ee37e,dt.span_id=e5e6748fab93ede8]2127.0.0.1 - [21/Oct/2021:10:33:31 +0200] GET /index.html HTTP/1.1 200 1056 [!dt dt.trace_id=81fe7816ba6c38f7aa09aef3684cd941,dt.span_id=3bdacc466ae073cd]
If you use a logging framework and log formatter that allows custom log patterns, you can adapt the pattern in the log formatter and directly access the Dynatrace enrichment attributes.
Example of manually enriching Log4j log data
In the Log4j PatternFormatter, you can specify a pattern like this to include Dynatrace enrichment information:
1<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} dt.trace_id=%X{dt.trace_id} dt.span_id=%X{dt.span_id} dt.entity.process_group_instance=%X{dt.entity.process_group_instance} - %msg%n"/>
Example of manually enriching Logstash Logback encoder
Logback is a successor to the log4j project. Logstash Logback is an extension that provides logback encoders, layouts, and appenders to log in JSON and other formats supported by Jackson.
The following is an example of manual enrichment using the Logstash encoder. Note the additional mdc
property in the configuration file, where you can include MDC variables.
1<appender name="COMPOSITEJSONENCODER" class="ch.qos.logback.core.FileAppender">2 <file>compositejsonencoder.log</file>3 <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">4 <providers>5 <timestamp>6 <fieldName>timestamp</fieldName>7 <timeZone>UTC</timeZone>8 </timestamp>9 <loggerName>10 <fieldName>logger</fieldName>11 </loggerName>12 <logLevel>13 <fieldName>level</fieldName>14 </logLevel>15 <threadName>16 <fieldName>thread</fieldName>17 </threadName>18 <mdc>19 <includeMdcKeyName>dt.span_id</includeMdcKeyName>20 <includeMdcKeyName>dt.trace_id</includeMdcKeyName>21 <includeMdcKeyName>dt.entity.host</includeMdcKeyName>22 </mdc>23 <stackTrace>24 <fieldName>stackTrace</fieldName>25 <!-- maxLength - limit the length of the stack trace -->26 <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">27 <maxDepthPerThrowable>200</maxDepthPerThrowable>28 <maxLength>14000</maxLength>29 <rootCauseFirst>true</rootCauseFirst>30 </throwableConverter>31 </stackTrace>32 <message />33 <throwableClassName>34 <fieldName>exceptionClass</fieldName>35 </throwableClassName>36 </providers>37 </encoder>38</appender>
NGINX ingress with Kubernetes
You can enrich your logs using NGINX ingress with Kubernetes in two steps:
-
Execute the ingress-nginx on Kubernetes instrumentation instructions.
-
Add the command below to the
configmap.yaml
file for NGINX ingress.Adding the
main-snippet
line enables OneAgent ingestion and is optional if you have followed the manual instrumentation instructions already.
1main-snippet: load_module /opt/dynatrace/oneagent/agent/bin/current/linux-musl-x86-64/liboneagentnginx.so;2log-format-upstream: '$remote_addr - $remote_user [$time_local] "$request" [!dt dt.trace_id=$dt_trace_id,dt.span_id=$dt_span_id,dt.trace_sampled=$dt_trace_sampled] $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length'
1apiVersion: v12kind: Namespace3metadata:4 name: prod-ingress-nginx5 labels:6 app.kubernetes.io/name: ingress-nginx7 app.kubernetes.io/instance: ingress-nginx89---10# Source: ingress-nginx/templates/controller-serviceaccount.yaml11apiVersion: v112kind: ServiceAccount13metadata:14 labels:15 helm.sh/chart: ingress-nginx-4.0.616 app.kubernetes.io/name: ingress-nginx17 app.kubernetes.io/instance: ingress-nginx18 app.kubernetes.io/version: 1.0.419 app.kubernetes.io/managed-by: Helm20 app.kubernetes.io/component: controller21 name: ingress-nginx22 namespace: prod-ingress-nginx23automountServiceAccountToken: true24---25# Source: ingress-nginx/templates/controller-configmap.yaml26apiVersion: v127kind: ConfigMap28metadata:29 labels:30 helm.sh/chart: ingress-nginx-4.0.631 app.kubernetes.io/name: ingress-nginx32 app.kubernetes.io/instance: ingress-nginx33 app.kubernetes.io/version: 1.0.434 app.kubernetes.io/managed-by: Helm35 app.kubernetes.io/component: controller36 name: ingress-nginx-controller37 namespace: prod-ingress-nginx38data:39 allow-snippet-annotations: 'true'40 main-snippet: load_module /opt/dynatrace/oneagent/agent/bin/current/linux-musl-x86-64/liboneagentnginx.so;41 log-format-upstream: '$remote_addr - $remote_user [$time_local] "$request" [!dt dt.trace_id=$dt_trace_id,dt.span_id=$dt_span_id,dt.trace_sampled=$dt_trace_sampled] $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length'42 ...
Retrieve span and trace IDs
To have Dynatrace match logs to corresponding traces, you can include the span and trace IDs in your log messages, using the [!dt]
notation.
The following examples show how to obtain the span and trace IDs with OpenTelemetry or the OneAgent SDK:
For details on configuration, see AWS Lambda logs in context of traces.
For instructions on how to source these attributes via OneAgent SDK:
- Go: see the GO documentation on GitHub
- .NET: see the .NET documentation on GitHub
Retrieve process group instance ID
You can get the dt.entity.process_group_instance
field using the OpenTelemetry Python command containing merged
. The process_group_instance
is retrieved as one of the attributes delivered in merged
, as shown in the example below:
With OneAgent, you can simply point to a local endpoint without an authentication token to enable trace ingestion.
1import json23from opentelemetry import trace as OpenTelemetry4from opentelemetry.exporter.otlp.proto.http.trace_exporter import (5 OTLPSpanExporter,6)7from opentelemetry.sdk.resources import Resource8from opentelemetry.sdk.trace import TracerProvider, sampling9from opentelemetry.sdk.trace.export import (10 BatchSpanProcessor,11)1213merged = dict()14for name in ["dt_metadata_e617c525669e072eebe3d0f08212e8f2.json", "/var/lib/dynatrace/enrichment/dt_metadata.json"]:15 try:16 data = ''17 with open(name) as f:18 data = json.load(f if name.startswith("/var") else open(f.read()))19 merged.update(data)20 except:21 pass2223merged.update({24 "service.name": "python-quickstart", #TODO Replace with the name of your application25 "service.version": "1.0.1", #TODO Replace with the version of your application26})27resource = Resource.create(merged)2829tracer_provider = TracerProvider(sampler=sampling.ALWAYS_ON, resource=resource)30OpenTelemetry.set_tracer_provider(tracer_provider)3132tracer_provider.add_span_processor(33 BatchSpanProcessor(OTLPSpanExporter(34 endpoint="http://localhost:14499/otlp/v1/traces"35 )))
When using OneAgent, make sure to enable the public Extension Execution Controller in your Dynatrace Settings, otherwise no data will be sent.
In the Dynatrace menu, go to Settings > Preferences > Extension Execution Controller. The toggles Enable Extension Execution Controller and Enable local PIPE/HTTP metric and Log Ingest API should be active.
For details on configuration, see Instrument your Python application with OpenTelemetry
Limitations
If you use a custom winston formatter/transport (applicable to Node.js only), you need to manually add your injected dt.traceId
and dt.spanId
as in the example below:
1const winston = require("winston");2const Transport = require("winston-transport");34class CustomTransport extends Transport {5 log(info, next) {6 let myLogLine = `MyLogLine: ${info.timestamp} level=${info.level}: ${info.message}`;7 // this is important as above line only picks timestamp, level and message but nothing else from metadata8 if (info["dt.trace_id"]) {9 myLogLine = `[!dt dt.trace_id=${info["dt.trace_id"]},dt.span_id=${info["dt.span_id"]},dt.trace_sampled=${info["dt.trace_sampled"]}] ${myLogLine}`;10 }11 console.log(myLogLine);12 next();13 }14}1516const logger = winston.createLogger({17 level: "info",18 format: winston.format.timestamp(),19 transports: [20 new CustomTransport(),21 // this transport includes all metadata (including dynatrace added traceId,..)22 new winston.transport.Console({23 format: winston.format.simple()24 })25 ]26})