Skip to content

Commit 9e6594a

Browse files
committed
OpenTelemetry tracing
1 parent 964d387 commit 9e6594a

File tree

13 files changed

+40
-34
lines changed

13 files changed

+40
-34
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88

99
### Using Docker compose
1010

11-
The fastest way to experiment with Bootzooka is using the provided Docker compose setup. It starts three images:
11+
The fastest way to experiment with Bootzooka is using the provided Docker compose setup. It starts three images:
1212
Bootzooka itself (either locally built or downloaded), PostgreSQL server and Graphana LGTM for observability.
1313

1414
### Backend: PostgreSQL & API
1515

16-
To run Bootzooka's backend locally, you'll still need a running instance of PostgreSQL with a `bootzooka` database.
16+
To run Bootzooka's backend locally, you'll still need a running instance of PostgreSQL with a `bootzooka` database.
1717
You can spin up one easily using docker:
1818

1919
```sh
@@ -44,12 +44,12 @@ Then, you can start the frontend:
4444
./frontend-start.sh
4545
```
4646

47-
And open `http://localhost:3000`.
47+
And open `http://localhost:8081`.
4848

4949
## Commercial Support
5050

5151
We offer commercial support for Bootzooka and related technologies, as well as development services. [Contact us](https://softwaremill.com) to learn more about our offer!
5252

5353
## Copyright
5454

55-
Copyright (C) 2013-2024 SoftwareMill [https://softwaremill.com](https://softwaremill.com).
55+
Copyright (C) 2013-2025 SoftwareMill [https://softwaremill.com](https://softwaremill.com).

backend/src/main/resources/application.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ email {
8888
}
8989

9090
password-reset {
91-
reset-link-pattern = "http://localhost:3000/password-reset?code=%s"
91+
reset-link-pattern = "http://localhost:8081/password-reset?code=%s"
9292
reset-link-pattern = ${?PASSWORD_RESET_LINK_PATTERN}
9393

9494
code-valid = 1 day

backend/src/main/scala/com/softwaremill/bootzooka/Main.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,28 @@ import com.softwaremill.bootzooka.logging.Logging
44
import ox.logback.InheritableMDC
55
import ox.{Ox, OxApp, never}
66
import org.slf4j.bridge.SLF4JBridgeHandler
7+
import ox.OxApp.Settings
8+
import ox.otel.context.PropagatingVirtualThreadFactory
79

810
object Main extends OxApp.Simple with Logging:
9-
// route JUL to SLF4J
11+
// route JUL to SLF4J (JUL is used by Magnum for logging)
1012
SLF4JBridgeHandler.removeHandlersForRootLogger()
1113
SLF4JBridgeHandler.install()
1214

15+
// https://ox.softwaremill.com/latest/integrations/mdc-logback.html
1316
InheritableMDC.init
17+
1418
Thread.setDefaultUncaughtExceptionHandler((t, e) => logger.error("Uncaught exception in thread: " + t, e))
1519

20+
// https://ox.softwaremill.com/latest/integrations/otel-context.html
21+
override protected def settings: Settings = Settings.Default.copy(threadFactory = PropagatingVirtualThreadFactory())
22+
1623
override def run(using Ox): Unit =
1724
val deps = Dependencies.create
1825

1926
deps.emailService.startProcesses()
20-
val binding = deps.httpApi.start()
21-
logger.info(s"Started Bootzooka on ${binding.hostName}:${binding.port}.")
27+
deps.httpApi.start()
28+
logger.info(s"Bootzooka started")
2229

2330
// blocking until the application is shut down
2431
never

backend/src/main/scala/com/softwaremill/bootzooka/http/HttpApi.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import sttp.tapir.server.metrics.opentelemetry.OpenTelemetryMetrics
1414
import sttp.tapir.server.model.ValuedEndpointOutput
1515
import sttp.tapir.server.netty.NettyConfig
1616
import sttp.tapir.server.netty.sync.{NettySyncServer, NettySyncServerBinding, NettySyncServerOptions}
17+
import sttp.tapir.server.tracing.opentelemetry.OpenTelemetryTracing
1718
import sttp.tapir.swagger.SwaggerUIOptions
1819
import sttp.tapir.swagger.bundle.SwaggerInterpreter
1920

@@ -40,6 +41,7 @@ class HttpApi(
4041

4142
private val serverOptions: NettySyncServerOptions = NettySyncServerOptions.customiseInterceptors
4243
.prependInterceptor(CorrelationIdInterceptor)
44+
.prependInterceptor(OpenTelemetryTracing(otel))
4345
// all errors are formatted as JSON, and no additional routes are added to the server
4446
.defaultHandlers(msg => ValuedEndpointOutput(Http.jsonErrorOutOutput, Error_OUT(msg)), notFoundWhenRejected = true)
4547
.corsInterceptor(CORSInterceptor.default[Identity])

backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/CorrelationId.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import com.softwaremill.bootzooka.logging.Logging
44
import org.slf4j.MDC
55
import ox.logback.InheritableMDC
66
import sttp.client3.*
7-
import sttp.client3.{Response, SttpBackend}
87
import sttp.capabilities.Effect
98
import sttp.monad.MonadError
109
import sttp.tapir.server.interceptor.{EndpointInterceptor, RequestHandler, RequestInterceptor, Responder}

backend/src/main/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetApi.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package com.softwaremill.bootzooka.passwordreset
33
import com.github.plokhotnyuk.jsoniter_scala.macros.ConfiguredJsonValueCodec
44
import com.softwaremill.bootzooka.http.Http.*
55
import com.softwaremill.bootzooka.infrastructure.DB
6-
import com.softwaremill.bootzooka.infrastructure.Magnum.*
76
import com.softwaremill.bootzooka.http.{EndpointsForDocs, ServerEndpoints}
87
import sttp.tapir.Schema
98

backend/src/main/scala/com/softwaremill/bootzooka/user/UserApi.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ import com.github.plokhotnyuk.jsoniter_scala.macros.ConfiguredJsonValueCodec
44
import com.softwaremill.bootzooka.Fail
55
import com.softwaremill.bootzooka.http.Http.*
66
import com.softwaremill.bootzooka.infrastructure.DB
7-
import com.softwaremill.bootzooka.infrastructure.Magnum.*
87
import com.softwaremill.bootzooka.metrics.Metrics
98
import com.softwaremill.bootzooka.security.{ApiKey, Auth}
109
import com.softwaremill.bootzooka.http.{EndpointsForDocs, ServerEndpoints}
1110
import com.softwaremill.bootzooka.util.Strings.{Id, asId}
1211
import sttp.tapir.*
13-
import sttp.tapir.json.jsoniter.jsonBody
1412

1513
import java.time.Instant
1614
import scala.concurrent.duration.*

build.sbt

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ val httpDependencies = Seq(
3434
val observabilityDependencies = Seq(
3535
"com.softwaremill.sttp.client3" %% "opentelemetry-metrics-backend" % sttpVersion,
3636
"com.softwaremill.sttp.tapir" %% "tapir-opentelemetry-metrics" % tapirVersion,
37+
"com.softwaremill.sttp.tapir" %% "tapir-opentelemetry-tracing" % tapirVersion,
38+
"com.softwaremill.ox" %% "otel-context" % oxVersion,
3739
"io.opentelemetry" % "opentelemetry-exporter-otlp" % otelVersion,
3840
"io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % otelVersion,
3941
"io.opentelemetry.instrumentation" % "opentelemetry-runtime-telemetry-java8" % otelInstrumentationVersion,
@@ -182,35 +184,33 @@ lazy val backend: Project = (project in file("backend"))
182184
configDependencies ++ dbDependencies ++ httpDependencies ++ jsonDependencies ++
183185
apiDocsDependencies ++ observabilityDependencies ++ securityDependencies ++ emailDependencies,
184186
Compile / mainClass := Some("com.softwaremill.bootzooka.Main"),
185-
copyWebapp := {
186-
val source = uiDirectory.value / "build"
187-
val target = (Compile / classDirectory).value / "webapp"
188-
streams.value.log.info(s"Copying the webapp resources from $source to $target")
189-
IO.copyDirectory(source, target)
190-
},
191-
copyWebapp := copyWebapp
192-
.dependsOn(
193-
Def
194-
.sequential(
195-
generateOpenAPIDescription,
196-
yarnTask.toTask(" build")
197-
)
198-
)
199-
.value,
187+
// generates the target/openapi.yaml file which is then used by the UI to generate service stubs
200188
generateOpenAPIDescription := Def.taskDyn {
201189
val log = streams.value.log
202190
val targetPath = ((Compile / target).value / "openapi.yaml").toString
203191
Def.task {
204192
(Compile / runMain).toTask(s" com.softwaremill.bootzooka.writeOpenAPIDescription $targetPath").value
205193
}
206194
}.value,
195+
// used by fat-jar and docker builds, to copy the UI files to a single bundle
196+
copyWebapp := {
197+
val source = uiDirectory.value / "build"
198+
val target = (Compile / classDirectory).value / "webapp"
199+
streams.value.log.info(s"Copying the webapp resources from $source to $target")
200+
IO.copyDirectory(source, target)
201+
},
202+
copyWebapp := copyWebapp.dependsOn(Def.sequential(generateOpenAPIDescription, yarnTask.toTask(" build"))).value,
203+
// used by backend-start.sh, to restart the application when sources change
204+
reStart := {
205+
generateOpenAPIDescription.value
206+
reStart.evaluated
207+
},
207208
// needed so that a ctrl+c issued when running the backend from the sbt console properly interrupts the application
208209
run / fork := true,
209210
scalacOptions ++= List("-Wunused:all", "-Wvalue-discard")
210211
)
211212
.enablePlugins(BuildInfoPlugin)
212213
.settings(commonSettings)
213-
.settings(Revolver.settings)
214214
.settings(buildInfoSettings)
215215
.settings(fatJarSettings)
216216
.enablePlugins(DockerPlugin)

docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ services:
1414
API_HOST: '0.0.0.0'
1515
OTEL_EXPORTER_OTLP_ENDPOINT: 'http://observability:4317'
1616
OTEL_SERVICE_NAME: 'bootzooka'
17-
OTEL_METRIC_EXPORT_INTERVAL: '500' # The default is 60s, in development it's useful to see the metrics faster
17+
OTEL_METRIC_EXPORT_INTERVAL: '500' # The default is 60s, in development it's useful to see the metrics faster
1818
OTEL_RESOURCE_ATTRIBUTES: 'service.instance.id=local,service.version=latest'
1919

2020
bootzooka-db:
@@ -31,3 +31,4 @@ services:
3131
image: 'grafana/otel-lgtm'
3232
ports:
3333
- '3000:3000' # Grafana's UI
34+
- '4317:4317' # Exporter

docs/frontend.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ If this is your first attempt to run `ui`, please go to `ui` project and run
2121

2222
yarn install
2323

24-
This will install all required dependencies for this project. If all is well you can start your development version of frontend by issuing `yarn start` from command line (or running the provided `frontend-start` script in the main directory). It should start your browser and point you to [Bootzooka home page](http://0.0.0.0:3000/#/).
24+
This will install all required dependencies for this project. If all is well you can start your development version of frontend by issuing `yarn start` from command line (or running the provided `frontend-start` script in the main directory). It should start your browser and point you to [Bootzooka home page](http://0.0.0.0:8081/#/).
2525

2626
## Development
2727

@@ -36,7 +36,7 @@ The most important tasks exposed are:
3636

3737
## `yarn start` task
3838

39-
This task serves Bootzooka application on port `3000` on `0.0.0.0` (it is available to all hosts from the same network). Your default browser should open at this location. All requests to the backend will be proxied to port `8080` where it expects the server to be run.
39+
This task serves Bootzooka application on port `8081` on `0.0.0.0` (it is available to all hosts from the same network). Your default browser should open at this location. All requests to the backend will be proxied to port `8080` where it expects the server to be run.
4040

4141
Hot reload is in place already (provided by the Vite stack), so every change is automatically compiled (if necessary) and browser is automatically refreshed to apply changes. No need to refresh it by hand.
4242

0 commit comments

Comments
 (0)