> NOTE: For how to *use* Sourcegraph's observability and an overview of our observability features, refer to the [observability for site administrators documentation](../../admin/observability/index.md).
1. A standardized, strongly-typed, and structured logging interface, [`log.Logger`](https://sourcegraph.com/github.com/sourcegraph/log/-/blob/logger.go)
1. Production output from this logger (`SRC_LOG_FORMAT=json`) complies with the [OpenTelemetry log data model](https://opentelemetry.io/docs/reference/specification/logs/data-model/) (also see: [Logging: OpenTelemetry](../../admin/observability/logs.md#opentelemetry))
3. A getter to retrieve a `log.Logger` instance, [`log.Scoped`](https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/sourcegraph/sourcegraph%24+log.Scoped+lang:go&patternType=literal), and [`(Logger).Scoped`](https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/sourcegraph/sourcegraph%24+logger.Scoped+lang:go&patternType=literal) for [creating sub-loggers](#sub-loggers).
> NOTE: Sourcegraph's new logging standards are still a work in progress — please [join our community](https://community.sourcegraph.com) if you have any feedback or ideas!
1.`github.com/sourcegraph/log` intentionally does not export directly usable (global) log functions. Users should hold their own references to `Logger` instances, so that fields can be attached and log output can be more useful with additional context, and pass `Logger`s to components as required.
1. Do not call the package-level `log.Scoped` wherever you need to log—consider passing a `Logger` around explicitly, and [creating sub-scoped loggers using `(Logger).Scoped()`](#scoped-loggers).
2.`github.com/sourcegraph/log` should export everything that is required for logging—do not directly import a third-party logging package such as `zap`, `log15`, or the standard `log` library.
- The logger parameter should either be immediately after `ctx context.Context`, or be the first parameter.
- In some cases there might already be a `log` module imported. Use the alias `sglog` to refer to `github.com/sourcegraph/log`, for example `import sglog "github.com/sourcegraph/log"`.
- When testing, provide [test loggers](#testing-usage) for improved output management.
- For more conventions, see relevant subsections in this guide, such as [top-level loggers](#top-level-loggers), [sub-loggers](#sub-loggers), and [writing log messages](#writing-log-messages).
### Top-level loggers
Once initialized, you can use `log.Scoped()` to create some top-level loggers to propagate. From each logger, you can:
The first top-level scoped logger is typically `"server"`, since most logging is related to server initialization and service-level logging—the name of the service itself is already logged as part of the [`Resource` field](https://opentelemetry.io/docs/reference/specification/logs/data-model/#field-resource) provided during initialization.
From a parent logger, you can create sub-loggers that have additional context attached to them using functions on the `log.Logger` instance that returns a new `log.Logger` instance:
1. [Scoped loggers](#scoped-loggers)
2. [Fields sub-loggers](#fields-sub-loggers)
3. [Traced sub-loggers](#traced-sub-loggers)
All the above mechanisms attach metadata to *all* log entries created by the sub-logger, and they do not modify the parent logger.
Using sub-loggers allows you to easily trace, for example, the execution of an event or a particular execution type by looking for shared log fields.
> WARNING: All sub-logger constructors and functions on the `log.Logger` type (e.g. `(Logger).Soped(...)`, `(Logger).With(...)`, `(Logger).WithTrace(...)`) **do not** modify the underlying logger—you must hold and use the reference to the returned `log.Logger` instance.
Scopes are used to identify the component of a system a log message comes from, and generally should provide enough information for an uninitiated reader (such as a new teammate, or a Sourcegraph administrator) to get a rough idea the context in which a log message might have occurred.
- a top-level scoped logger, from the package-level `log.Scoped()` function, is used mostly in a `main()`-type function or in places where no logger has been propagated.
- a scoped sub-logger, which can be created from an existing logger with `(Logger).Scoped()`. The sub-scope is appended onto the parent scope with a `.` separator.
In general:
- From the caller, only add a scope if, as a caller, you care that the log output enough to want to differentiate it
- For example, if you create multiple clients for a service, you will probably want to create a separate scoped logger for each
- From the callee, add a scope if you will be logging output that should be meaningfully differentiated (e.g. inside a client, or inside a significant helper function)
- Scope names should be `CamelCase` or `camelCase`, and the scope description should follow [the same conventions as a log message](#writing-log-messages).
In a particular scope, some fields might be repeatedly emitted—most commonly, you might want to associate a set of log entries to an ID of some sort (user ID, iteration ID, etc). In these scenarios you should create a sub-logger with prepended fields by using `logger.With(...fields)`, and use it directly to maintain relevant context:
1. [`internal/trace.Logger(ctx, logger)`](https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/blob/internal/trace/logger.go) creates a sub-logger with trace context from `context.Context`
If writing log messages that, for example, indicate the results of a function, simply use the Go conventions for naming (i.e. just copy the function name).
If multiple log lines have similar components (such as a message prefix, or the same log fields) prefer to [create a sub-logger](#sub-loggers) instead. For example:
- instead of repeating a message prefix to e.g. indicate a component, [create a scoped sub-logger](#scoped-loggers) instead
- instead of adding the same field to multiple log calls, [create a fields sub-logger](#fields-sub-loggers) instead
> WARNING: Field constructors provided by the `sourcegraph/log` package, for example `log.Error(error)`, **do not** create log entries—they create fields (`type log.Field`) that are intended to be provided to `Logger` functions like `(Logger).Info` and so on.
If the optional sink [`log.NewSentrySink()`](https://doctree.org/github.com/sourcegraph/log/-/go/-//?id=NewSentrySink) is passed when initializing the logger, when an error is passed in a field to the logger with `log.Error(err)`, it will be reported to Sentry automatically if and only if the log level is above or equal to `Warn`.
The log message and all fields will be used to annotate the error report and the logger scope will be used as a tag, which being indexed by Sentry, enables to group reports.
For example, the Sentry search query `is:unresolved scope:*codeintel*` will surface all error reports coming from errors that were logged by loggers whose scope includes `codeintel`.
If multiple error fields are passed, an individual report will be created for each of them.
The Sentry project to which the reports are sent is configured through the `log.sentry.backendDSN` site-config entry.
With `SRC_DEVELOPMENT=true` and `SRC_LOG_FORMAT=condensed` or `SRC_LOG_FORMAT=console`, loggers will generate a human-readable summary format like the following:
This format omits fields like OpenTelemetry `Resource` and renders certain field types in a more friendly manner. Levels are also coloured, and the caller link with `filename:line` should be clickable in iTerm and VS Code such that you can jump straight to the source of the log entry.
1. A getter to retrieve a `log.Logger` instance and a callback to programmatically iterate log output, [`logtest.Scoped`](https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/sourcegraph/sourcegraph%24+logtest.Scoped+lang:go&patternType=literal)
3. Can be provided in code that accepts [injected loggers](#injected-loggers)
2. An *optional* initialization function to be called in `func TestMain(*testing.M)`, [`logtest.Init`](https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/sourcegraph/sourcegraph%24+logtest.Init+lang:go&patternType=literal).
If the code you are testing accepts `Logger` instances as a parameter, you can skip the above and simply use `logtest.Scoped` to instantiate a `Logger` to provide. You can also use `logtest.Captured`, which also provides a callback for exporting logs, though be wary of making overly strict assertions on log entries to avoid brittle tests:
When writing a test, ensure that `logtest.Scope` in the tightest scope possible. This ensures that if a test fails, that the logging is closely tied to the test that failed. Especially if you testcase has sub tests with `t.Run`, prefer to created the test logger inside `t.Run`.
Alternatively, `logtest.NoOp()` creates a logger that can be used to silence output. Levels can also be adjusted using `(Logger).IncreaseLevel`.
### Instantiated loggers
In the absense of `log.Init` in `main()`, testing code that instantiates loggers with `log.Scoped` (as opposed to `(Logger).Scoped`), `github.com/sourcegraph/log` can be initialized using `libtest` in packages that use `log.Scoped`:
```go
import (
"os"
"testing"
"github.com/sourcegraph/log/logtest"
)
func TestMain(m *testing.M) {
logtest.Init(m)
os.Exit(m.Run())
}
```
You can control the log level used during initialization with `logtest.InitWithLevel`.