WeMakeDevs x SigNoz: "Agents of SigNoz" Hackathon 2026
Silent failures and healthy logs
Before configuring OpenTelemetry and SigNoz, my application appeared perfectly healthy. Health checks passed, and the API responded normally.
Underneath, however, a batch of orders disappeared every few minutes. The system did not throw exceptions or print error messages to stdout. The revenue loss went entirely unnoticed.
I wrote the openSlice simulator to investigate this problem. I wanted to see how a payment provider failure impacts on 7 Microservices e-commerce system, and if I could expose these silent issues in less than 10 minutes using SigNoz and OpenTelemetry.
The objective was to set up OpenTelemetry in a Python application, export the data to SigNoz, and use trace waterfalls and error metrics to diagnose incidents.
The openSlice simulator
The openSlice tool simulates a Python e-commerce backend. It uses threads and latency models instead of real databases or HTTP calls, generating OTLP telemetry without running a full microservices cluster.
https://github.com/thisisvaishnav/openSlice
Service topology
api-gateway -> (root span)
├──-> auth-service -> product-catalog
├──-> cart-service
├──-> order-service -> payment-service (failing) -> notification
└──-> product-catalog
Each service runs in its own thread with a separate TracerProvider and MeterProvider to mimic independent microservices. The services pass context using W3C Trace Context headers through OpenTelemetry's inject and extract functions, allowing SigNoz to construct the full trace waterfall.
The simulator's chaos engine randomly triggers one of four incident modes in the payment service:
| Incident | Behavior |
|---|---|
DEGRADED |
Latency increases to 2-5 seconds, 30% error rate |
PARTIAL_OUTAGE |
60% of requests fail |
PROVIDER_FAILURE |
All Stripe requests fail; other payment methods remain healthy |
MEMORY_PRESSURE |
Latency increases linearly over 45 seconds to 3,000ms |
When a provider failure occurs, the order service also fails because it depends on the payment service to complete transactions. This cascading failure is what we want SigNoz to help us detect.
Running SigNoz locally
You can run SigNoz using Docker Desktop without a cloud account or a Kubernetes cluster.
1. Start SigNoz
Clone the SigNoz repository and start the services using Docker Compose. This runs ClickHouse, the OTel Collector, and the SigNoz UI.
git clone https://github.com/SigNoz/signoz.git
cd signoz/deploy
docker compose up -d
2. Install Python dependencies
Install the OpenTelemetry SDK, OTLP HTTP exporter, and protobuf packages.
# requirements.txt
opentelemetry-sdk==1.34.1
opentelemetry-exporter-otlp-proto-http==1.34.1
opentelemetry-api==1.34.1
protobuf==5.29.4
cd ~/openSlice
pip install -r requirements.txt
3. Start the simulator
Run the simulator to start sending traces and metrics to your local SigNoz instance.
# Chaos mode is enabled by default
python platform_simulator.py
# To disable incident injection:
python platform_simulator.py --no-chaos
# To point to a remote SigNoz endpoint:
python platform_simulator.py --endpoint http://your-signoz:4318
Verifying the simulator execution
After starting the simulator, the terminal displays a live status table that refreshes every 10 seconds. This table shows which services are active and whether chaos injection is running.
Figure 1: Terminal output from the simulator showing service health status.
The SigNoz UI is accessible at http://localhost:8080. It takes about a minute for the services to show up in the Services tab. You do not need to register them manually.
How to instrument the simulator code
The simulator uses the ServiceThread class to represent each service. It initializes the OpenTelemetry providers and wraps requests in spans.
1. Separate TracerProviders for each service
SigNoz separates services using the service.name attribute on the OpenTelemetry resource. If your services share a TracerProvider, SigNoz will group them together in the dashboard. Each service must have its own provider with a distinct resource:
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
self.resource = Resource.create({
"service.name": self.service_name,
"service.version": "1.0.0",
"deployment.environment": "production",
})
trace_exporter = OTLPSpanExporter(endpoint=f"{endpoint}/v1/traces")
self.tracer_provider = TracerProvider(resource=self.resource)
self.tracer_provider.add_span_processor(BatchSpanProcessor(trace_exporter))
self.tracer = self.tracer_provider.get_tracer(self.service_name)
2. Distributing trace context
The API gateway creates the root span and injects the W3C traceparent header into the request headers. Downstream services extract this header, linking their spans as children of the gateway's span.
from opentelemetry.propagate import inject, extract
# API Gateway: starts the root span and injects context
with self.tracer.start_as_current_span(
f"{self.service_name}/handle_request", kind=SpanKind.SERVER
) as gateway_span:
for downstream in self.downstream:
headers = {}
inject(headers)
downstream._handle_downstream(headers)
# Downstream service: extracts the context and creates a child span
def _handle_downstream(self, parent_headers: dict):
ctx = extract(parent_headers)
with self.tracer.start_as_current_span(
f"{self.service_name}/process",
context=ctx,
kind=SpanKind.SERVER,
) as span:
# Process the request
Note: If you do not pass context=ctx during downstream span creation, the spans will show up in SigNoz as separate root traces rather than a linked parent-child waterfall.
3. Setting up metrics
Each service collects request counts, error counts, latency, and rolling error rates.
# OpenTelemetry metrics instruments
self.request_counter = self.meter.create_counter(f"{self.service_name}.requests", unit="1")
self.error_counter = self.meter.create_counter(f"{self.service_name}.errors", unit="1")
self.latency_histogram = self.meter.create_histogram(f"{self.service_name}.latency", unit="ms")
# Observable gauge to calculate and emit the rolling error rate
def make_callback(svc_name, s):
def callback(options):
snap = s.snapshot()
err_rate = snap["err_count"] / max(snap["total"], 1)
yield metrics.Observation(err_rate, {"service.name": svc_name})
return callback
st.meter.create_observable_gauge(
name=f"{st.service_name}.error_rate",
callbacks=[make_callback(st.service_name, svc_stats)],
unit="1",
)
4. Adding custom span attributes for chaos events
During simulated incidents, the simulator adds the active incident type to the span. This allows you to filter and query traces in SigNoz directly.
# Add the incident type to the span
if self.service_name == "payment-service" and self.incident_state.active:
span.set_attribute("incident.type", self.incident_state.incident_type)
# Mark Stripe requests as failed during a provider failure
if (self.service_name == "payment-service"
and self.incident_state.incident_type == "PROVIDER_FAILURE"
and self._is_stripe_request()):
span.set_attribute("payment.method", "stripe")
return True
Viewing telemetry in SigNoz
When the simulator starts running, the SigNoz dashboard visualizes the telemetry.
Services dashboard
The Services page lists all seven simulated services. SigNoz maps service dependencies automatically. When errors occur in the payment service, the UI highlights the service and shows how the issues affect the upstream order service.
Trace explorer and request waterfalls
To view trace details, navigate to the Trace Explorer, filter by service: api-gateway, and select a trace.
SigNoz connects the parent and child spans. You can see the api-gateway make concurrent requests to downstream services, with the order service calling the payment service. If a provider failure happens, the payment service span highlights in red with an error status.
Figure 2: SigNoz Trace Explorer displaying a failed payment service span.
Metrics dashboard
You can build custom dashboards in SigNoz to track metrics. A two-panel dashboard can monitor P99 latency and error rates across services.
| Metric | Healthy | During incident |
|---|---|---|
| P99 Latency (payment-service) | ~400ms | 4,500ms |
| Error Rate (payment-service) | ~5% | 95% |
| Error Rate (order-service) | ~4% | 20% (cascading failure) |
| Other services | baseline | unchanged |
Figure 3: Dashboard monitoring payment service latency and cascading error rates in the order service.
Querying trace attributes
Debugging a payment service failure usually requires searching through application logs and matching timestamps to find the transaction details.
In SigNoz, clicking on a failed span displays its custom attributes in the side panel. For example, during a Stripe outage, the span contains the attributes payment.method: stripe and incident.type: PROVIDER_FAILURE. This allows you to identify the specific error source without searching through log files.
Lessons learned
- Each service needs its own TracerProvider. Sharing a TracerProvider combines multiple services into one entity in SigNoz. The OpenTelemetry specification identifies services by the resource object, making individual providers necessary.
- Context propagation is required. If you do not pass the context to child spans, the parent-child relationships break. The trace waterfall then displays as a flat list of unrelated spans.
- Use span attributes over text logs. Adding details like the incident type and payment method as attributes allows you to filter and aggregate traces directly in SigNoz.
- Focus on key metrics. Tracking P99 latency and error rates is usually sufficient to identify and locate failures. Adding more dashboards often increases noise rather than clarity.
Summary
Using SigNoz, the simulated payment outage was visible in seconds. The UI highlighted the latency spike, the elevated error rate, and the specific failure attributes on the traces.
Setting up SigNoz via Docker and configuring the simulator took under 30 minutes. This configuration provides tracing and metrics out of the box.
If you are developing Python services, implementing OpenTelemetry helps capture silent failures that standard log files might miss.
To test the setup, clone the openSlice repository, start SigNoz, and run the simulator to view the injected failures in the dashboard.
Resources
- SigNoz Docker quickstart
- SigNoz instrumentation documentation
- OpenTelemetry semantic conventions
- SigNoz query builder
- SigNoz alerts
- WeMakeDevs Hackathon resources
Written for the WeMakeDevs and SigNoz "Agents of SigNoz" Hackathon 2026 | openSlice project



Top comments (1)
please give feedback about this blog