Logs

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the previous lessons, all the metrics and traces you sent were with the aim to have a visual representation. There is still a need for ordinary text information. You’re probably used to reading logs from your users, you may have generated some logs and sent them through a feedback form in your app.

OpenTelemetry can make this easier to collect, and you can still benefit from the visual graphs of traces and metrics for your logs.

To get started, open the starter project for the lesson. The project has the implementation for OTelLogs and the setup needed to connect the log exporter to Grafana, just as you did before with metrics and spans.

Add your token and endpoint to the Common.swift file so the app can connect to your server.

Sending a log works almost the same as sending a metric. Open OTelLogs.swift and add this at the end of the class:


public func sendLog(
  scope: String,
  message: String
) {
  let openTelemetry = OpenTelemetry.instance
  let otelLogger = openTelemetry.loggerProvider.loggerBuilder(instrumentationScopeName: scope).setEventDomain("Device").build()
  let log = otelLogger.logRecordBuilder()
    .setBody(.string(message))
  log.emit()
}

There is nothing here that you haven’t seen before with metrics. You create a logger from the loggerBuilder method. The setBody() creates an instance of the log itself, then finally calling emit() to send the log.

For convenience, create a class method for sendLog(::):

public class func sendLog(
  scope: String,
  message: String
) {
  shared.sendLog(
    scope: scope,
    message: message)
}

Go to TheMetStore.swift and add log at the end of fetchObjects(for:):

OTelLogs.sendLog(
  scope: "TheMet-Logs",
  message: "Searched for \(queryTerm), found \(objects.count) objects")

Build and run the app, you can search a couple of times for different keywords.

Open your Grafana portal and open “Logs”. You’ll find “TheMet” as one of the services.

The 'Logs' portal showing the 'TheMet' service with the log we just entered
The 'Logs' portal showing the 'TheMet' service with the log we just entered

Click on the “Show logs” button to see the logs better. Then click on the “Table” tab on the right.

The 'Table' of the 'Log' showing the 'Time' and the 'Line' of each log
The 'Table' of the 'Log' showing the 'Time' and the 'Line' of each log

The graph on the top represents the amount of logs over time. The checkboxes on the left allow you to choose which fields to show in the table. Feel free to add a few.

This log you sent is meant to be an example, it’s not meant to be used like print(). That will make your logs list messy and harder to understand. Feel free to remove it.

Another way to make logs easy to understand is to give them context beside the content in the log itself. You can connect logs to spans almost the same way you connect spans together.

Change sendLog to the following:

public func sendLog(
  scope: String,
  message: String,
  span: (any Span)? = nil
) {
  let openTelemetry = OpenTelemetry.instance
  let otelLogger = openTelemetry.loggerProvider.loggerBuilder(instrumentationScopeName: scope).setEventDomain("Device").build()
  let log = otelLogger.logRecordBuilder()
    .setBody(.string(message))
  if let span {
    _ = log.setSpanContext(span.context)
  }
  log.emit()
}

Remember to update the class method too:

public class func sendLog(
  scope: String,
  message: String,
  span: (any Span)? = nil
) {
  shared.sendLog(
    scope: scope,
    message: message,
    span: span)
}

Go back to TheMetStore.swift and move the call to send the log before ending the span, and add the span as a parameter in the function call:

OTelLogs.sendLog(
  scope: "TheMet-Logs",
  message: "Searched for \(queryTerm), found \(objects.count) objects",
  span: span)

Build and run the app. Open Logs on Grafana, and you’ll see the logs reachable from there. A new field called “span_id” will appear in the scrollable “Fields” list on the left.

Go to “Traces” and open the main span for “fetchObjects”. Clicking the button “Logs for this span” will show only the logs connected to this span.

The main span for 'fetchObjects' with the 'Logs for this span' button
The main span for 'fetchObjects' with the 'Logs for this span' button

You can have multiple logs attached to a span.

See forum comments
Download course materials from Github
Previous: Introduction Next: Events