Send OpenTelemetry Logs with gRPCurl

OpenTelemetry
Author

Bill Meyer

Published

October 10, 2024

Overview

OpenTelemetry uses the OTLP protocol to receive telemetry data (logs, metrics and traces) as well as export it.

Furthermore, it can receive and export OTLP over two underlying transports:

  • gRPC - A protobuf-based, binary protocol listening on default port 4317,
  • HTTP - An HTTP-based protocol listening on default port 4318.

Most OpenTelemetry users are familiar with using curl to send logs, metrics, and traces to an OpenTelemetry Collector’s OTLP/HTTP port (4318). Often the question comes up on doing the same, but to the gRPC port (4317). Because, gRPC is a binary protocol built using protobuf definitions, curl cannot be used.

However, grpcurl is a command-line tool used to communicate with gRPC services similar to curl.

This example will use gRPCurl to send a JSON OpenTelemetry Log Event to an OpenTelemetry Collector over the OTLP gRPC port.

Prerequisites

This exercise requires git and grpcurl to be installed.

Telemetry Format

In order to send a metric, log or trace to an OpenTelemetry Collector via either HTTP or gRPC, we first need a JSON-formatted payload of the metric, log or trace. The easiest way to get this is to either use the fileexporter exporter to write JSON-formatted events to a file, or simply download some samples from the OpenTelemetry docs.

Example Log Event

For this exercise, we’ll use this Log event:

{
  "resourceLogs": [
    {
      "resource": {},
      "scopeLogs": [
        {
          "scope": {},
          "logRecords": [
            {
              "observedTimeUnixNano": "1664827200000000000",
              "body": {
                "stringValue": "🪵🪵🪵🪵 This is the sample log message."
              },
              "attributes": [
                {
                  "key": "log.file.name",
                  "value": {
                    "stringValue": "access_log"
                  }
                }
              ],
              "traceId": "",
              "spanId": ""
            }
          ]
        }
      ]
    }
  ]
}

Clone OpenTelemetry protobuf definitions

To get started, we need to first download the protobufs for the Collector to our local computer:

mkdir $HOME/exercise
cd $HOME/exercise
git clone https://github.com/open-telemetry/opentelemetry-proto.git

Sending with gRPCurl

From within the $HOME/exercise directory, execute the following:

Note
  • Be sure to replace <collector-hostname> with the hostname or IP address of the Collector,
  • The -plaintext option is only needed when the Collector does not use TLS encryption.
# Send a log to an OpenTelemetry Collector
grpcurl \
  -plaintext \
  -v \
  -d @ \
  -proto opentelemetry-proto/opentelemetry/proto/collector/logs/v1/logs_service.proto \
  -import-path ./opentelemetry-proto \
  <collector-hostname>:4317 \
  opentelemetry.proto.collector.logs.v1.LogsService/Export <<EOF
{
  "resourceLogs": [
    {
      "resource": {},
      "scopeLogs": [
        {
          "scope": {},
          "logRecords": [
            {
              "observedTimeUnixNano": "1664827200000000000",
              "body": {
                "stringValue": "🪵🪵🪵🪵 This is the sample log message."
              },
              "attributes": [
                {
                  "key": "log.file.name",
                  "value": {
                    "stringValue": "access_log"
                  }
                }
              ],
              "traceId": "",
              "spanId": ""
            }
          ]
        }
      ]
    }
  ]
}
EOF

Output:

Resolved method descriptor:
// For performance reasons, it is recommended to keep this RPC
// alive for the entire life of the application.
rpc Export ( .opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest ) returns ( .opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse );

Request metadata to send:
(empty)

Response headers received:
content-type: application/grpc

Response contents:
{
  "partialSuccess": {}
}

Response trailers received:
(empty)
Sent 1 request and received 1 response

If you have configured a detailed-level debug exporter in your Collector:

exporters:
  debug/detailed:
    verbosity: detailed

service:
  pipelines:
    logs:
      receivers:
        ...
      processors:
        ...
      exporters:
        - debug/detailed

you should see the output appear in the Collector’s output:

2024-10-11T08:20:30.377-0500    info    LogsExporter    {"kind": "exporter", "data_type": "logs", "name": "debug/detailed", "resource logs": 1, "log records": 1}
2024-10-11T08:20:30.378-0500    info    ResourceLog #0
Resource SchemaURL:
Resource attributes:
     -> env: Str(otel)
     -> datadog.host.use_as_metadata: Bool(true)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope
LogRecord #0
ObservedTimestamp: 2022-10-03 20:00:00 +0000 UTC
Timestamp: 1970-01-01 00:00:00 +0000 UTC
SeverityText:
SeverityNumber: Unspecified(0)
Body: Str(🪵🪵🪵🪵 This is the sample log message.)
Attributes:
     -> log.file.name: Str(access_log)
     -> env: Str(otel)
Trace ID:
Span ID:
Flags: 0
    {"kind": "exporter", "data_type": "logs", "name": "debug/detailed"}
No matching items