An open telemetry collector processor that will symbolicate JavaScript stack traces using source maps. This is compatible with v0.12.0 and onwards of the Honeycomb OpenTelemetry web SDK.
To install this processor, include it in the build config file of your OpenTelemetry collector distro. You can also use the pre-built Honeycomb OpenTelemetry collector distro that already includes this processor.
- We require the use of CGO for the processors. If you are building your own collector image it must support glibc. We recommend
gcr.io/distroless/cc
Register the plugin in the processors section of your open telemetry collector configuration.
processors:
symbolicator:
The symbolicator requires access to both your minimised JavaScript source files
and associated JavaScript source map files generated by your build process.
Ideally these files are versioned with a file hash in the file name.
eg. main.c383b093b0b66825a9c3.js
. The minimised source file must also contain
the sourceMappingURL
tag containing the relative path to the source map file.
The symbolicator can access these files through a number of different storage
mechanisms documented below.
The processor expects the stacktrace information to be formatted into four separate attributes:
- columns
- functions
- lines
- urls
Each of these attributes must be a slice with each being of equal length.
Example:
columns: [6465,3512358,3512661,3514018,758545]
functions: ["?","w.callback","push.Br+g.w.crossDomainError","XMLHttpRequest.<anonymous>","XMLHttpRequest.<anonymous>"]
lines: [3582,2,2,2,2]
urls: ["https://example.com/static/dist/main.c383b093b0b66825a9c3.js","https://example.com/static/dist/vendor.1c285a50f5307be9648d.js","https://example.com/static/dist/vendor.1c285a50f5307be9648d.js","https://example.com/static/dist/vendor.1c285a50f5307be9648d.js","https://example.com/static/dist/vendor.1c285a50f5307be9648d.js"]
The following configuration options can also be provided to change the attributes used to look for stack traces and store them.
Config Key | Description | Example Value |
---|---|---|
symbolicator_failure_attribute_key |
Signals if the the symbolicator fails to fully symbolicate the stack trace | exception.symbolicator.failed |
symbolicator_failure_message_attribute_key |
Contains the error message if the the symbolicator fails to fully symbolicate the stack trace | exception.symbolicator.error |
columns_attribute_key |
Which attribute should the columns of the stack trace be sourced from | exception.structured_stacktrace.columns |
functions_attribute_key |
Which attribute should the functions of the stack trace be sourced from | exception.structured_stacktrace.functions |
lines_attribute_key |
Which attribute should the lines of the stack trace be sourced from | exception.structured_stacktrace.lines |
urls_attribute_key |
Which attribute should the urls of the stack trace be sourced from | exception.structured_stacktrace.urls |
output_stack_trace_key |
Which attribute should the symbolicated stack trace be populated into | exception.stacktrace |
stack_type_key |
Which attribute contains the exception type | exception.type |
stack_message_key |
Which attribute contains the exception message | exception.message |
preserve_stack_trace |
After the stack trace has been symbolicated should the original values be preserved as attributes | true |
original_stack_trace_key |
If the stack trace is being preserved which key should it be copied to | exception.stacktrace.original |
original_columns_attribute_key |
If the stack trace is being preserved which key should the functions be copied to | exception.structured_stacktrace.functions.original |
original_functions_attribute_key |
If the stack trace is being preserved which key should the lines be copied to | exception.structured_stacktrace.lines.original |
original_lines_attribute_key |
If the stack trace is being preserved which key should the columns be copied to | exception.structured_stacktrace.columns.original |
original_urls_attribute_key |
If the stack trace is being preserved which key should the urls be copied to | exception.structured_stacktrace.urls.original |
Config Key | Description | Example Value |
---|---|---|
timeout |
Max duration to wait to symbolicate a stack trace in seconds. | 5 |
source_map_cache_size |
The maximum number of source maps to cache. Reduce this if you are running into memory issues with the collector. | 128 |
The collector processor emits custom telemetry metrics that provides insight into its status and performance. The custom processor metrics are generated using mdatagen and include metrics such as:
- Number of stack frames processed
- Number of stack frames that failed to symbolicate
- Total source map fetch failures
- The size of the source map cache in bytes
To see the full list of the custom telemetry collected, see documentation.md in the symbolicatorprocessor
package.
To actually send your internal telemetry to a backend, make sure to enable the metrics
service in your collector's yaml config file. Here is an example of a
collector config sending metrics to a Honeycomb endpoint:
# ...
service:
metrics:
readers:
- periodic:
exporter:
otlp:
protocol: http/protobuf
endpoint: "https://api.honeycomb.io:443" # OR, for EU instance: https://api.eu1.honeycomb.io:443
headers:
- name: "x-honeycomb-team"
value: "HONEYCOMB_API_KEY"
- name: "x-honeycomb-dataset"
value: "otel-collector-metrics"
#...
Internal telemetry is collected by the collector by default and will be sent alongside our custom telemetry. Please visit Open Telemetry's Collector docs for a full list of the default internal metrics.
Register the plugin in the processors section of your open telemetry collector configuration.
processors:
dsymsymbolicator:
The symbolicator requires access to the dSYM file generated by the build process.
This file must be versioned with the generated build UUID in the file name.
eg. 6A8CB813-45F6-3652-AD33-778FD1EAB196.dSYM
. The symbolicator can access this dSYM file
through a number of different storage mechanisms documented below.
Given an .xcarchive
file generated by Xcode, you can get this build uuid with the
dwarfdump
tool. For example, the following script will find the latest build and
upload it to the app-archives
s3 bucket with the ios
prefix:
example script
# Get the App Name
if [[ -z "$1" ]]; then
echo "❌ Usage: $0 <TargetName>"
exit 1
fi
TARGET_NAME=$1
export ARCHIVE_PATH=$(ls -dt ~/Library/Developer/Xcode/Archives/*/"$TARGET_NAME"*.xcarchive | head -1)
echo "📦 Using Archive Path: $ARCHIVE_PATH"
if [[ ! -d "$ARCHIVE_PATH" ]]; then
echo "❌ Archive not found for target: $TARGET_NAME! Please archive the project first in Xcode."
exit 1
fi
find "$ARCHIVE_PATH/dSYMs" -name "*.dSYM" | while read line ; do
echo "🔍 Found dsym at: $line"
dsymuuid=$(dwarfdump -u "$line" | awk '{ print $2 }').dSYM
echo "⬆️ Uploading dsym to: $dsymuuid"
aws s3 cp --recursive "$line" s3://app-archives/ios/$dsymuuid
done
The processor processes incoming logs and expects the stacktrace information to be formatted one of two formats: generic stack traces or metrickit reports.
Generic stacktraces must include the following attributes:
exception.stacktrace
app.debug.build_uuid
app.bundle.executable
The processor will use app.debug.build_uuid
to look up a corresponding dSYM, and use
app.bundle.executable
to find the relevant binary archive within that dSYM. It will then
attempt to symbolicate as many lines of exception.stacktrace
as it can.
Any lines in exception.stacktrace
that refer to unknown binaries will be left as-is.
Additionally, while they are not required or used by the processor, exception.message
and exception.type
attributes are required by OTel semantic conventions, and most
downstream error sinks will expect them to be present.
MetricKit logs must include the metrickit.diagnostic.crash.exception.stacktrace_json
attribute,
corresponding to the jsonRepresentation
of a MetricKit crash.
The processor will use binaryUUID
and binaryName
contained in that JSON report
to look up a corresponding dSYM and find the relevant binary archive within that dSYM.
It will then attempt to symbolicate as many frames of the crash report as it can.
Any frames that refer to unknown binaries will be left as-is.
The following configuration options can also be provided to change the attributes used to look for stack traces and store them.
Config Key | Description | Example Value |
---|---|---|
symbolicator_failure_attribute_key |
Signals if the the symbolicator fails to fully symbolicate the stack trace | exception.symbolicator.failed |
symbolicator_failure_message_attribute_key |
Contains the error message if the the symbolicator fails to fully symbolicate the stack trace | exception.symbolicator.error |
stack_trace_attribute_key |
Which attribute should the stack trace of a generic stacktrace log be sourced from | exception.stacktrace |
original_stack_trace_key |
If the stack trace is being preserved, which key should it be copied to | exception.stacktrace.original |
build_uuid_attribute_key |
Which resource attribute should the binary UUID of a generic stacktrace log be sourced from | app.debug.build_uuid |
app_executable_attribute_key |
Which resource attribute should the name of the app executable of a generic stacktrace log be sourced from | app.bundle.executable |
metrickit_stack_trace_attribute_key |
Which attribute should the json representation of a metrickit stacktrace log be sourced from | metrickit.diagnostic.crash.exception.stacktrace_json |
output_metrickit_stack_trace_attribute_key |
Which attribute should the symbolicated metrickit stack trace be populated into | exception.stacktrace |
output_metrickit_exception_type_attribute_key |
Which attribute should the exception type be populated into | exception.type . |
output_metrickit_exception_message_attribute_key |
Which attribute should the exception message be populated into | exception.message . |
preserve_stack_trace |
After the stack trace has been symbolicated should the original values be preserved as attributes | true |
Config Key | Description | Example Value |
---|---|---|
timeout |
Max duration to wait to symbolicate a stack trace in seconds. | 5 |
dsym_cache_size |
The maximum number of dSYMs to cache. Reduce this if you are running into memory issues with the collector. | 128 |
Register the plugin in the processors section of your open telemetry collector configuration.
processors:
proguard_symbolicator:
The symbolicator requires access to the Proguard file generated by the build process.
This file must be versioned with the generated build UUID in the file name.
eg. 6A8CB813-45F6-3652-AD33-778FD1EAB196.txt
. The symbolicator can access this proguard file
through a number of different storage mechanisms documented below.
The processor expects the stacktrace information to be formatted into three separate attributes:
- classes
- methods
- lines
Each of these attributes must be a slice with each being of equal length.
Example:
classes: ["com.example.Test", "com.example.Test"]
methods: ["methodA", "methodB"]
lines: [3582, 2001]
The following configuration options can also be provided to change the attributes used to look for stack traces and store them.
Config Key | Description | Example Value |
---|---|---|
symbolicator_failure_attribute_key |
Signals if the the symbolicator fails to fully symbolicate the stack trace | exception.symbolicator.failed |
classes_attribute_key |
Which attribute should the classes of the stack trace be sourced from | exception.structured_stacktrace.classes |
methods_attribute_key |
Which attribute should the methods of the stack trace be sourced from | exception.structured_stacktrace.methods . |
lines_attribute_key |
Which attribute should the lines of the stack trace be sourced from | exception.structured_stacktrace.lines |
output_stack_trace_key |
Which attribute should the symbolicated stack trace be populated into | exception.stacktrace |
preserve_stack_trace |
After the stack trace has been symbolicated should the original values be preserved as attributes | true |
original_classes_attribute_key |
If the stack trace is being preserved which key should the classes be copied to | exception.structured_stacktrace.classes.original |
original_methods_attribute_key |
If the stack trace is being preserved which key should the methods be copied to | exception.structured_stacktrace.methods.original |
original_lines_attribute_key |
If the stack trace is being preserved which key should the lines be copied to | exception.structured_stacktrace.lines.original |
original_stack_trace_key |
If the stack trace is being preserved which key should it be copied to | exception.stacktrace.original |
proguard_uuid_attribute_key |
Which resource attribute should the proguard UUID of a generic stacktrace log be sourced from | app.debug.proguard_uuid |
Config Key | Description | Example Value |
---|---|---|
timeout |
Max duration to wait to symbolicate a stack trace in seconds. | 5 |
proguard_cache_size |
The maximum number of proguard files to cache. Reduce this if you are running into memory issues with the collector. | 128 |
Both the Source Map and dSYM processors use the same configuration formats for loading the requires files. The examples below are written for Source Map storage, but may easily be adapted to dSYM storage by swapping the names of the configuration keys:
Source Map Key | dSYM storage equivalent | proguard storage equivalent |
---|---|---|
source_map_store |
dsym_store |
proguard_store |
local_source_maps |
local_dsyms |
local_store |
s3_source_maps |
s3_dsyms |
s3_store |
gcs_source_maps |
gcs_dsyms |
gcs_store |
The default configuration will load the source(map) files (this must include both the JavaScript source file and the JavaScript source map file) from a local path on disk. You can set the base path that will be used.
processors:
symbolicator:
# source_map_store is used to configure which store to use, in this case local disk
source_map_store: file_store
local_source_maps:
# (optional) path is used to set the base path of the files, defaults to `.`
path: /tmp/sourcemaps
Each line of the stack trace includes the URL of the file it originated from.
Taking this as an example https://example.com/static/dist/main.c383b093b0b66825a9c3.js
.
The base file name is then found main.c383b093b0b66825a9c3.js
.
This path is joined with the configured path and then read from disk.
You can also load the source(map) files (this must include both the JavaScript source file and the JavaScript source map file) from an S3 bucket.
processors:
symbolicator:
# source_map_store is used to configure which store to use, in this case S3
source_map_store: s3_store
# s3_source_maps is used to configure the sourcing of source maps from S3
s3_source_maps:
# bucket is the name of the bucket the files are stored in
bucket: source-maps-bucket
# (optional) region is used to configure the buckets location
region: us-east-1
# (optional) prefix is used to nest the files in a sub key of the bucket
prefix: source-maps
Each line of the stack trace includes the URL of the file it originated from.
Taking this as an example https://example.com/static/dist/main.c383b093b0b66825a9c3.js
.
The base file name is then found main.c383b093b0b66825a9c3.js
.
This path is joined with the prefix if provided and then used as the key to
source from the bucket.
You can also load the source(map) files from a GCS bucket.
processors:
symbolicator:
# source_map_store is used to configure which store to use, in this case GCS
source_map_store: gcs_store
# gcs_source_maps is used to configure the sourcing of source maps from GCS
gcs_source_maps:
# bucket is the name of the bucket the files are stored in
bucket: source-maps-bucket
# (optional) prefix is used to nest the files in a sub key of the bucket
prefix: source-maps
Each line of the stack trace includes the URL of the file it originated from.
Taking this as an example https://example.com/static/dist/main.c383b093b0b66825a9c3.js
.
The base file name is then found main.c383b093b0b66825a9c3.js
.
This path is joined with the prefix if provided and then used as the key to
source from the bucket.