Skip to content

Commit

Permalink
[Go] Document logs (#4463)
Browse files Browse the repository at this point in the history
Co-authored-by: Patrice Chalin <[email protected]>
Co-authored-by: Severin Neumann <[email protected]>
  • Loading branch information
3 people authored May 16, 2024
1 parent 3acf78c commit 12f31f6
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 5 deletions.
169 changes: 166 additions & 3 deletions content/en/docs/languages/go/instrumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ aliases:
- manual_instrumentation
weight: 30
description: Manual instrumentation for OpenTelemetry Go
cSpell:ignore: fatalf otlptrace sdktrace sighup
cSpell:ignore: fatalf logr logrus otelslog otlplog otlploghttp sdktrace sighup
---

{{% docs/languages/instrumentation-intro %}}
Expand Down Expand Up @@ -37,7 +37,6 @@ import (
"log"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
Expand Down Expand Up @@ -876,7 +875,161 @@ meterProvider := metric.NewMeterProvider(

## Logs

The logs API is currently unstable, documentation TBA.
Logs are distinct from metrics and traces in that **there is no user-facing
OpenTelemetry logs API**. Instead, there is tooling to bridge logs from existing
popular log packages (such as slog, logrus, zap, logr) into the OpenTelemetry
ecosystem. For rationale behind this design decision, see
[Logging specification](/docs/specs/otel/logs/).

The two typical workflows discussed below each cater to different application
requirements.

### Direct-to-Collector

**Status**: [Experimental](/docs/specs/otel/document-status/)

In the direct-to-Collector workflow, logs are emitted directly from an
application to a collector using a network protocol (e.g. OTLP). This workflow
is simple to set up as it doesn't require any additional log forwarding
components, and allows an application to easily emit structured logs that
conform to the [log data model][log data model]. However, the overhead required
for applications to queue and export logs to a network location may not be
suitable for all applications.

To use this workflow:

- Configure the OpenTelemetry [Log SDK](#logs-sdk) to export log records to
desired target destination (the [collector][opentelemetry collector] or
other).
- Use an appropriate [Log Bridge](#log-bridge).

#### Logs SDK

The logs SDK dictates how logs are processed when using the
[direct-to-Collector](#direct-to-collector) workflow. No log SDK is needed when
using the [log forwarding](#via-file-or-stdout) workflow.

The typical log SDK configuration installs a batching log record processor with
an OTLP exporter.

To enable [logs](/docs/concepts/signals/logs/) in your app, you'll need to have
an initialized [`LoggerProvider`](/docs/concepts/signals/logs/#logger-provider)
that will let you use a [Log Bridge](#log-bridge).

If a `LoggerProvider` is not created, the OpenTelemetry APIs for logs will use a
no-op implementation and fail to generate data. Therefore, you have to modify
the source code to include the SDK initialization code using the following
packages:

- [`go.opentelemetry.io/otel`][]
- [`go.opentelemetry.io/otel/sdk/log`][]
- [`go.opentelemetry.io/otel/sdk/resource`][]
- [`go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`][]

Ensure you have the right Go modules installed:

```sh
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/otel/sdk/log
```

Then initialize a logger provider:

```go
package main

import (
"context"
"fmt"

"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
"go.opentelemetry.io/otel/log/global"
"go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
)

func main() {
ctx := context.Background()

// Create resource.
res, err := newResource()
if err != nil {
panic(err)
}

// Create a logger provider.
// You can pass this instance directly when creating bridges.
loggerProvider, err := newLoggerProvider(ctx, res)
if err != nil {
panic(err)
}

// Handle shutdown properly so nothing leaks.
defer func() {
if err := loggerProvider.Shutdown(ctx); err != nil {
fmt.Println(err)
}
}()

// Register as global logger provider so that it can be accessed global.LoggerProvider.
// Most log bridges use the global logger provider as default.
// If the global logger provider is not set then a no-op implementation
// is used, which fails to generate data.
global.SetLoggerProvider(loggerProvider)
}

func newResource() (*resource.Resource, error) {
return resource.Merge(resource.Default(),
resource.NewWithAttributes(semconv.SchemaURL,
semconv.ServiceName("my-service"),
semconv.ServiceVersion("0.1.0"),
))
}

func newLoggerProvider(ctx context.Context, res *resource.Resource) (*log.LoggerProvider, error) {
exporter, err := otlploghttp.New(ctx)
if err != nil {
return nil, err
}
processor := log.NewBatchProcessor(exporter)
provider := log.NewLoggerProvider(
log.WithResource(res),
log.WithProcessor(processor),
)
return provider, nil
}
```

Now that a `LoggerProvider` is configured, you can use it to set up a
[Log Bridge](#log-bridge).

#### Log Bridge

A log bridge is a component that bridges logs from an existing log package into
the OpenTelemetry [Log SDK](#logs-sdk) using the [Logs Bridge
API][logs bridge API]. Log bridges are available for various popular Go log
packages:

- [logrus bridge][otellogrus]
- [slog bridge][otelslog]

The links above contain full usage and installation documentation.

### Via file or stdout

In the file or stdout workflow, logs are written to files or standout output.
Another component (e.g. FluentBit) is responsible for reading / tailing the
logs, parsing them to more structured format, and forwarding them a target, such
as the collector. This workflow may be preferable in situations where
application requirements do not permit additional overhead from
[direct-to-Collector](#direct-to-collector). However, it requires that all log
fields required down stream are encoded into the logs, and that the component
reading the logs parse the data into the [log data model][log data model]. The
installation and configuration of log forwarding components is outside the scope
of this document.

## Next Steps

Expand All @@ -887,11 +1040,21 @@ telemetry backends.
[opentelemetry specification]: /docs/specs/otel/
[trace semantic conventions]: /docs/specs/semconv/general/trace/
[instrumentation library]: ../libraries/
[opentelemetry collector]:
https://github.com/open-telemetry/opentelemetry-collector
[logs bridge API]: /docs/specs/otel/logs/bridge-api
[log data model]: /docs/specs/otel/logs/data-model
[otellogrus]: https://pkg.go.dev/go.opentelemetry.io/contrib/bridges/otellogrus
[otelslog]: https://pkg.go.dev/go.opentelemetry.io/contrib/bridges/otelslog
[`go.opentelemetry.io/otel`]: https://pkg.go.dev/go.opentelemetry.io/otel
[`go.opentelemetry.io/otel/exporters/stdout/stdoutmetric`]:
https://pkg.go.dev/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric
[`go.opentelemetry.io/otel/metric`]:
https://pkg.go.dev/go.opentelemetry.io/otel/metric
[`go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`]:
https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp
[`go.opentelemetry.io/otel/sdk/log`]:
https://pkg.go.dev/go.opentelemetry.io/otel/sdk/log
[`go.opentelemetry.io/otel/sdk/metric`]:
https://pkg.go.dev/go.opentelemetry.io/otel/sdk/metric
[`go.opentelemetry.io/otel/sdk/resource`]:
Expand Down
4 changes: 2 additions & 2 deletions content/en/docs/languages/go/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ cSpell:ignore: sdktrace thirdparty

{{% docs/languages/resources-intro %}}

Resources should be assigned to a tracer provider at its initialization, and are
created much like attributes:
Resources should be assigned to a tracer, meter, and logger provider at its
initialization, and are created much like attributes:

```go
res := resource.NewWithAttributes(
Expand Down
12 changes: 12 additions & 0 deletions static/refcache.json
Original file line number Diff line number Diff line change
Expand Up @@ -7007,6 +7007,14 @@
"StatusCode": 200,
"LastSeen": "2024-03-01T16:49:37.76693+01:00"
},
"https://pkg.go.dev/go.opentelemetry.io/contrib/bridges/otellogrus": {
"StatusCode": 200,
"LastSeen": "2024-05-15T16:22:37.734630943Z"
},
"https://pkg.go.dev/go.opentelemetry.io/contrib/bridges/otelslog": {
"StatusCode": 200,
"LastSeen": "2024-05-10T11:02:15.508410781Z"
},
"https://pkg.go.dev/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp": {
"StatusCode": 200,
"LastSeen": "2024-01-19T15:36:28.468246594Z"
Expand Down Expand Up @@ -7071,6 +7079,10 @@
"StatusCode": 200,
"LastSeen": "2024-01-30T15:25:42.951557-05:00"
},
"https://pkg.go.dev/go.opentelemetry.io/otel/sdk/log": {
"StatusCode": 200,
"LastSeen": "2024-05-10T11:02:08.878761042Z"
},
"https://pkg.go.dev/go.opentelemetry.io/otel/sdk/metric": {
"StatusCode": 200,
"LastSeen": "2024-01-30T15:25:12.503352-05:00"
Expand Down

0 comments on commit 12f31f6

Please sign in to comment.