it

SigNoz Observability Clean Architecture

SigNoz Observability Clean Architecture

SigNoz Observability Clean Architecture คืออะไร

SigNoz Observability Clean Architecture

SigNoz เป็น open-source observability platform ที่รวม metrics, traces และ logs ไว้ในที่เดียว เป็นทางเลือกแทน Datadog, New Relic ที่ไม่ต้องจ่ายค่า license แพง ใช้ OpenTelemetry เป็น standard สำหรับเก็บข้อมูล และ ClickHouse เป็น storage backend ที่เร็วมาก Clean Architecture คือหลักการออกแบบ software ที่แยก business logic ออกจาก infrastructure ทำให้ testable, maintainable และ flexible การรวม SigNoz observability เข้ากับ Clean Architecture ช่วยให้ระบบ observable โดยไม่ทำลายโครงสร้าง code

SigNoz Architecture

# signoz_arch.py — SigNoz architecture overview
import json

class SigNozArchitecture:
    COMPONENTS = {
        "otel_collector": {
            "name": "OpenTelemetry Collector",
            "role": "รับ telemetry data (metrics, traces, logs) จาก applications",
            "protocol": "OTLP (gRPC/HTTP), Jaeger, Zipkin, Prometheus",
        },
        "query_service": {
            "name": "Query Service",
            "role": "API สำหรับ query data จาก ClickHouse — serve ให้ frontend",
            "tech": "Go-based, REST API",
        },
        "clickhouse": {
            "name": "ClickHouse",
            "role": "Storage backend — column-oriented DB ที่เร็วมากสำหรับ time-series",
            "benefit": "Query traces/logs เร็ว, compression ดี, scale ได้",
        },
        "frontend": {
            "name": "Frontend (React)",
            "role": "Dashboard UI — แสดง metrics, traces, logs, alerts",
            "features": "Service map, trace waterfall, log explorer, custom dashboards",
        },
        "alertmanager": {
            "name": "Alert Manager",
            "role": "จัดการ alerts — ส่ง notifications ผ่าน Slack, PagerDuty, email",
        },
    }

    PILLARS = {
        "metrics": {
            "name": "Metrics",
            "description": "ตัวเลขที่วัดได้: request count, latency, error rate, CPU, memory",
            "signoz": "ส่งผ่าน OTLP → เก็บใน ClickHouse → แสดงบน dashboard",
        },
        "traces": {
            "name": "Traces (Distributed Tracing)",
            "description": "ติดตาม request ข้าม services — ดู latency แต่ละ service",
            "signoz": "OpenTelemetry auto-instrumentation → trace waterfall view",
        },
        "logs": {
            "name": "Logs",
            "description": "ข้อความ log จาก applications — structured/unstructured",
            "signoz": "ส่งผ่าน OTLP/Fluentd → ClickHouse → Log Explorer",
        },
    }

    def show_components(self):
        print("=== SigNoz Components ===\n")
        for key, comp in self.COMPONENTS.items():
            print(f"[{comp['name']}]")
            print(f"  {comp['role']}")
            print()

    def show_pillars(self):
        print("=== Three Pillars ===")
        for key, pillar in self.PILLARS.items():
            print(f"\n[{pillar['name']}] {pillar['description']}")

arch = SigNozArchitecture()
arch.show_components()
arch.show_pillars()

Clean Architecture Principles

# clean_arch.py — Clean Architecture with observability
import json

class CleanArchitecture:
    LAYERS = {
        "entities": {
            "name": "Entities (Domain)",
            "description": "Business rules และ domain objects — ไม่ขึ้นกับ framework หรือ DB",
            "observability": "ไม่ควรมี observability code ใน layer นี้",
        },
        "use_cases": {
            "name": "Use Cases (Application)",
            "description": "Application business rules — orchestrate entities",
            "observability": "Trace spans สำหรับ business operations, business metrics",
        },
        "interface_adapters": {
            "name": "Interface Adapters",
            "description": "Controllers, Presenters, Gateways — แปลงระหว่าง layers",
            "observability": "HTTP metrics, request/response logging, error tracking",
        },
        "frameworks": {
            "name": "Frameworks & Drivers",
            "description": "Web framework, DB, external services — outermost layer",
            "observability": "Auto-instrumentation: HTTP, DB queries, external calls",
        },
    }

    DEPENDENCY_RULE = {
        "rule": "Dependencies point inward — outer layers depend on inner layers, never the reverse",
        "observability_approach": "Inject observability via interfaces — inner layers define interfaces, outer layers implement with tracing/metrics",
    }

    def show_layers(self):
        print("=== Clean Architecture Layers ===\n")
        for key, layer in self.LAYERS.items():
            print(f"[{layer['name']}]")
            print(f"  {layer['description']}")
            print(f"  Observability: {layer['observability']}")
            print()

    def show_rule(self):
        print("=== Dependency Rule ===")
        print(f"  Rule: {self.DEPENDENCY_RULE['rule']}")
        print(f"  Approach: {self.DEPENDENCY_RULE['observability_approach']}")

ca = CleanArchitecture()
ca.show_layers()
ca.show_rule()

Python Implementation

SigNoz Observability Clean Architecture
# implementation.py — Clean Architecture with SigNoz observability
import json

class CleanArchObservability:
    CODE = """
# clean_arch_observability.py — Clean Architecture with OpenTelemetry
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Optional
import time

# === Setup OpenTelemetry (Frameworks layer) ===
def setup_telemetry(service_name="my-service", endpoint="http://signoz:4317"):
    # Traces
    trace_provider = TracerProvider()
    trace_provider.add_span_processor(
        BatchSpanProcessor(OTLPSpanExporter(endpoint=endpoint, insecure=True))
    )
    trace.set_tracer_provider(trace_provider)
    
    # Metrics
    metric_reader = PeriodicExportingMetricReader(
        OTLPMetricExporter(endpoint=endpoint, insecure=True)
    )
    meter_provider = MeterProvider(metric_readers=[metric_reader])
    metrics.set_meter_provider(meter_provider)

# === Entities Layer (no observability) ===
@dataclass
class Order:
    id: str
    user_id: str
    amount: float
    status: str = "pending"
    
    def validate(self):
        if self.amount <= 0:
            raise ValueError("Amount must be positive")
        return True

# === Use Case Layer (observability via interface) ===
class OrderRepository(ABC):
    @abstractmethod
    def save(self, order: Order) -> bool:
        pass
    
    @abstractmethod
    def find_by_id(self, order_id: str) -> Optional[Order]:
        pass

class PaymentGateway(ABC):
    @abstractmethod
    def charge(self, user_id: str, amount: float) -> bool:
        pass

class Telemetry(ABC):
    '''Observability interface — defined in use case layer'''
    @abstractmethod
    def start_span(self, name: str):
        pass
    
    @abstractmethod
    def record_metric(self, name: str, value: float, labels: dict = None):
        pass

class CreateOrderUseCase:
    def __init__(self, repo: OrderRepository, payment: PaymentGateway, telemetry: Telemetry):
        self.repo = repo
        self.payment = payment
        self.telemetry = telemetry
    
    def execute(self, user_id: str, amount: float) -> Order:
        with self.telemetry.start_span("create_order") as span:
            # Create order
            order = Order(id="order_123", user_id=user_id, amount=amount)
            order.validate()
            
            # Process payment
            with self.telemetry.start_span("process_payment"):
                success = self.payment.charge(user_id, amount)
                if not success:
                    self.telemetry.record_metric("order_payment_failed", 1)
                    raise Exception("Payment failed")
            
            # Save order
            with self.telemetry.start_span("save_order"):
                order.status = "confirmed"
                self.repo.save(order)
            
            self.telemetry.record_metric("order_created", 1, {"status": "success"})
            self.telemetry.record_metric("order_amount", amount)
            
            return order

# === Interface Adapter Layer (implements observability) ===
class OpenTelemetryAdapter(Telemetry):
    '''Implements Telemetry interface using OpenTelemetry'''
    def __init__(self, service_name="order-service"):
        self.tracer = trace.get_tracer(service_name)
        self.meter = metrics.get_meter(service_name)
        self._counters = {}
        self._histograms = {}
    
    def start_span(self, name: str):
        return self.tracer.start_as_current_span(name)
    
    def record_metric(self, name: str, value: float, labels: dict = None):
        if name not in self._counters:
            self._counters[name] = self.meter.create_counter(name)
        self._counters[name].add(value, labels or {})

# === Frameworks Layer (wiring) ===
# setup_telemetry("order-service", "http://signoz:4317")
# telemetry = OpenTelemetryAdapter("order-service")
# use_case = CreateOrderUseCase(repo, payment, telemetry)
# order = use_case.execute("user_1", 99.99)
"""

    def show_code(self):
        print("=== Clean Architecture + Observability ===")
        print(self.CODE[:600])

impl = CleanArchObservability()
impl.show_code()

SigNoz Dashboard & Alerts

# dashboard.py — SigNoz dashboard and alerting
import json

class SigNozDashboard:
    DASHBOARDS = {
        "service_overview": {
            "name": "Service Overview",
            "panels": [
                "P99 Latency by service",
                "Error rate by service",
                "Request rate (RPM)",
                "Service dependency map",
            ],
        },
        "business_metrics": {
            "name": "Business Metrics",
            "panels": [
                "Orders created per minute",
                "Payment success/failure rate",
                "Average order amount",
                "Revenue per hour",
            ],
        },
        "infrastructure": {
            "name": "Infrastructure",
            "panels": [
                "CPU/Memory usage by pod",
                "Database query latency",
                "External API response times",
                "Queue depth and processing time",
            ],
        },
    }

    ALERTS = {
        "high_error_rate": {
            "name": "High Error Rate",
            "condition": "error_rate > 5% for 5 minutes",
            "severity": "critical",
            "channel": "PagerDuty + Slack #incidents",
        },
        "high_latency": {
            "name": "High P99 Latency",
            "condition": "p99_latency > 2000ms for 10 minutes",
            "severity": "warning",
            "channel": "Slack #alerts",
        },
        "payment_failures": {
            "name": "Payment Failure Spike",
            "condition": "payment_failed_rate > 3% for 3 minutes",
            "severity": "critical",
            "channel": "PagerDuty + Slack #payments",
        },
    }

    def show_dashboards(self):
        print("=== SigNoz Dashboards ===\n")
        for key, dash in self.DASHBOARDS.items():
            print(f"[{dash['name']}]")
            for panel in dash['panels'][:3]:
                print(f"  • {panel}")
            print()

    def show_alerts(self):
        print("=== Alerts ===")
        for key, alert in self.ALERTS.items():
            print(f"\n[{alert['name']}] ({alert['severity']})")
            print(f"  Condition: {alert['condition']}")
            print(f"  Channel: {alert['channel']}")

dash = SigNozDashboard()
dash.show_dashboards()
dash.show_alerts()

Docker Compose Setup

# setup.py — SigNoz Docker Compose setup
import json

class SigNozSetup:
    DOCKER_COMPOSE = """
# docker-compose.yaml — SigNoz with application
version: '3.8'

services:
  # SigNoz (all-in-one for dev)
  signoz:
    image: signoz/signoz:latest
    ports:
      - "3301:3301"   # Frontend
      - "4317:4317"   # OTLP gRPC
      - "4318:4318"   # OTLP HTTP
    volumes:
      - signoz-data:/var/lib/signoz
    environment:
      - SIGNOZ_INSTRUMENTATION_KEY=your-key

  # Application with OpenTelemetry
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz:4317
      - OTEL_SERVICE_NAME=order-service
      - OTEL_RESOURCE_ATTRIBUTES=deployment.environment=dev
    depends_on:
      - signoz

volumes:
  signoz-data:
"""

    INSTRUMENTATION = """
# requirements.txt additions for OpenTelemetry
opentelemetry-api
opentelemetry-sdk
opentelemetry-exporter-otlp-proto-grpc
opentelemetry-instrumentation-fastapi
opentelemetry-instrumentation-sqlalchemy
opentelemetry-instrumentation-requests
opentelemetry-instrumentation-redis
"""

    def show_compose(self):
        print("=== Docker Compose ===")
        print(self.DOCKER_COMPOSE[:400])

    def show_instrumentation(self):
        print("\n=== Dependencies ===")
        print(self.INSTRUMENTATION[:300])

setup = SigNozSetup()
setup.show_compose()
setup.show_instrumentation()

FAQ - คำถามที่พบบ่อย

Q: SigNoz กับ Grafana Stack (Loki + Tempo + Mimir) อันไหนดีกว่า?

อ่านเพิ่ม: Monitoring คืออะไร? สอน Observability ตั้งแต่ Prometheus Gra · อ่านเพิ่ม: Prometheus และ Grafana คืออะไร? สอนสร้าง Monitoring Stack สำ · อ่านเพิ่ม: Microservices คืออะไร? สอนออกแบบ Microservices Architecture

เนื้อหาเกี่ยวข้อง — บทความที่เกี่ยวข้อง: Medusa Commerce GreenOps Sustainability

A: SigNoz: all-in-one, ง่ายกว่า deploy, ClickHouse backend เร็ว, correlated view (metrics+traces+logs) Grafana Stack: flexible มาก, community ใหญ่, plugins เยอะ, แต่ต้อง setup หลาย components เลือก SigNoz: ถ้าต้องการ simplicity + correlated observability + ไม่อยาก manage หลาย tools เลือก Grafana: ถ้าต้องการ flexibility + มี team ที่ manage ได้ + ใช้ Grafana อยู่แล้ว

แนะนำเพิ่มเติม — หนังสือเทรดที่ SiamCafeBook

Q: Clean Architecture เพิ่ม overhead ไหม?

เนื้อหาเกี่ยวข้อง — ดูเพิ่มเติมเรื่อง Data Lakehouse Consensus Algorithm

A: Code มากขึ้น: ต้องสร้าง interfaces, adapters, แยก layers — boilerplate เยอะกว่า Performance: overhead น้อยมาก — function call overhead เล็กน้อย ข้อดี: testable (mock ได้ทุก dependency), maintainable, เปลี่ยน infrastructure ง่าย คุ้มเมื่อ: project ใหญ่ + ทีม > 3 คน + อายุ > 1 ปี ไม่คุ้มเมื่อ: prototype, hackathon, project เล็กที่ทำคนเดียว

Q: Observability code ควรอยู่ layer ไหน?

แนะนำเพิ่มเติม — สัญญาณเทรดรายวัน XM Signal

เนื้อหาเกี่ยวข้อง — Kafka Connect GreenOps Sustainability

A: Entities: ไม่ควรมี observability code เลย Use Cases: define Telemetry interface + ใช้ record business metrics Interface Adapters: implement Telemetry interface ด้วย OpenTelemetry Frameworks: auto-instrumentation (HTTP, DB, external calls) + setup หลัก: ใช้ Dependency Injection — inner layers ไม่รู้จัก OpenTelemetry ตรงๆ

Q: SigNoz ฟรีไหม?

เนื้อหาเกี่ยวข้อง — แนะนำให้อ่าน Java Spring Security

A: SigNoz Community: ฟรี 100% open-source — self-host ได้ SigNoz Cloud: มี free tier (ข้อมูล 30 วัน) + paid plans Self-host: ต้อง manage infrastructure เอง (ClickHouse + SigNoz) Cloud: managed — ไม่ต้อง manage infrastructure, แต่มีค่าใช้จ่ายตาม data volume เริ่มต้น: Docker Compose สำหรับ dev → Kubernetes Helm chart สำหรับ production

XM Legend · เทรดเดอร์ & ผู้สอน Forex 13 ปี

ผู้ก่อตั้ง SiamCafe ตั้งแต่ปี 1997 · เทรดเดอร์สาย Forex มากกว่า 13 ปี ได้รับการยกย่องเป็น XM Legend · แบ่งปันความรู้ Forex, ไอที, AI และการเทรด จากประสบการณ์จริงในตลาดจริง