Events
Starting a search with a keyword represents an event, while receiving the results represents another event. You have a span that represents both of those 2 moments. But in many cases you’ll want to mark a moment in time with something that happened with some data to give insight on what happened.
Another use for events is sending a one-time value where a gauge doesn’t fit, since gauges present a continuous value for the app. One-off values are like when the app goes to the background or foreground, opens a screen, or receives a value from a server or a different framework. These are things related to a single moment in time with a certain value, and not about time duration.
OpenTelemetry treats Events almost the same as logs. The API has slight differences between each to focus on whats important to define an event, and for a log.
Go to OTelLogs.swift and add the following at the end of the class:
public func sendEvent(
scope: String,
eventName: String,
data: [String: AttributeValue],
message: String = "",
span: (any Span)? = nil
) {
let openTelemetry = OpenTelemetry.instance
let otelLogger = openTelemetry.loggerProvider.loggerBuilder(instrumentationScopeName: scope).setEventDomain("Device") .build()
let event = otelLogger.eventBuilder(name: eventName)
.setData(data)
.setBody(.string(message))
if let span {
_ = event.setSpanContext(span.context)
}
event.emit()
_ = grafanaExporter.flush()
}
public class func sendEvent(
scope: String,
eventName: String,
data: [String: AttributeValue],
message: String = "",
span: (any Span)? = nil
) {
shared.sendEvent(
scope: scope,
eventName: eventName,
data: data,
message: message,
span: span)
}
The main difference in sendEvent() is that an event should have a name and attributes. The message isn’t very important but there is nothing preventing you from adding one if you feel it’s valuable.
You’re probably familiar with Business Analytics platforms like Google Analytics for example. Those business events and your OpenTelemetry events are not much different. The key difference is that those analytics platforms are tailored for business use-cases and have a lot of convenient tooling and ready dashboards to give business insights on sales, user retention, app engagement, … etc. You can do the same ofc with OpenTelemetry and Grafana, but you’d be spending too much time to re-invent the wheel. :]
Go to TheMetStore.swift and add the following right after sending the OTel Log:
OTelLogs.sendEvent(
scope: "TheMet-Logs",
eventName: "objects_fetched",
data: ["value" : AttributeValue.int(objects.count)])
The AttributeValue is an enum to help maintain the data types of attribute values during serialization. It’s defined in OpenTelemetryApi. Make sure to add the import for OpenTelemetryApi to be able to use this AttributeValue.
Build and run the app. Search a few times to send a few events.
Open your Grafana portal, and open the logs for “TheMet”
You’ll see the events and logs all together.
Grafana won’t create a graph on its own for the values you’re sending in those event because it doesn’t know about them. Why not create a custom dashboard for this?
From the left menu on Grafana, open “Dashboards”, then choose “New dashboard” from the “New” dropdown in the top right corner
Select “Add visualization”, then choose “grafanacloud-[YOUR_PORTAL_NAME]_logs” as the datasource.
In the bottom half, in “Label filters” choose “service_name” as key field dropdown, and “TheMet” as the value from the dropdown. Press “Run query”
This will list all the logs coming from your app. You want to filter out the events that are named “objects_fetched” and create a visual graph from them.
In the middle of the screen, select the second tab “Transformations” then add the first transformation of type “Extract fields”
Set “labels” as the source, and “JSON” as the format
This allows Grafana to extract all the custom values present in the log.
Then add another transformation of type “Convert field type”. Set field to “event_data_value” from the field dropdown, and “Number” for the type.
Go back to the “Queries” tab, and change the view to “Code” instead of “Builder” besides the “Run qurey” button. Set the query to {service_name="TheMet"} | event_name="objects_fetched"
Once you see the correct filtered log entries, choose “Time series” visualization option from the search field on the right of the screen. Change the view on the top to graph by turning off “Table view” switch.
This will automatically convert the event entries to a visual representation.
Grafana recognized which values to graph because it found a “Number” type in one of the values which you did through the second transformation.
You might be thinking, If Grafana is treating logs and events almost the same way then what’s the difference?
The answer is not necessarily technical. Logs and events can both share the same technical data structure and have their similarities.
Logs are meant to describe the state of the system when something happens. A crash log is clear enough that the event is a crash, but what was happening in the app is what is important. As for an Event, it focuses on providing data for the event itself, like a purchase event. It doesn’t matter what the state of the application is, how much memory, how many screens, the battery state, the network state, … etc. All this is not important. What is important is what item the user purchased, the category, price, … etc. And events need to be structured because they provide a lot of information when you put them together and they are meant to be processed in quantity. However, logs can be very valuable as individual entries.