Skip to content

๐Ÿ“ˆ Observability (Logs & Metrics) โ€” optional enhancement

โš ๏ธ Note: this section is not yet in the codebase, but is recommended for developers who want better visibility.

You can add lightweight observability to GoMT4:

  • ๐ŸŒ obs.go helper โ€” wrapper around log.Printf so logs have consistent format (INFO/WARN/ERROR + timestamp).
  • โฑ Latency timers โ€” measure RPC duration (e.g., ShowQuote, ShowOrderSend).
  • ๐Ÿ“Š Counters โ€” simple atomic metrics for errors, ticks, RPC calls (print every 10s).
  • ๐Ÿงฉ Prometheus exporter โ€” optional, if you want Grafana dashboards.

Make GoMT4 debuggable in minutes, not hours. Below are pragmatic logging patterns and lightweight metrics that fit your current codebase (examples/mt4/*.go, main.go) and Windows setup.


๐ŸŽฏ Goals

  • See: what RPCs happen, how often, with what latency.
  • Spot: reconnect loops, stream stalls, broker rejections.
  • Prove: system is healthy via simple counters/ratios.

๐Ÿงญ Logging levels (simple & effective)

Use three coarse levels via the standard log pkg (no heavy deps):

  • INFO โ€” highโ€‘level events (connect, subscribe, order actions).
  • WARN โ€” transient issues (retry, backoff, deadline exceeded).
  • ERROR โ€” final failures (stream aborted, order rejected).

Helper:

package obs
import (
  "fmt"; "log"; "time"
)
func ts() string { return time.Now().Format(time.RFC3339) }
func Info(msg string, a ...any)  { log.Printf("INFO  %s | "+msg, append([]any{ts()}, a...)...) }
func Warn(msg string, a ...any)  { log.Printf("WARN  %s | "+msg, append([]any{ts()}, a...)...) }
func Error(msg string, a ...any) { log.Printf("ERROR %s | "+msg, append([]any{ts()}, a...)...) }

Place in examples/mt4/obs/obs.go (or inline now). Replace current fmt.Println(...) in hot paths.

Redaction (from Security & Secrets): never print passwords; mask tokens.


๐Ÿ”Œ Streams โ€” what to log

Target minimal but useful lines.

StreamQuotes (examples/mt4/MT4_service.go):

obs.Info("stream.quotes.start symbols=%v", symbols)
// on first N ticks only (sampling)
if ticks%100 == 0 { obs.Info("stream.quotes.rate ticks=%d", ticks) }
// on error
obs.Error("stream.quotes.error err=%v", err)
// on normal end
obs.Info("stream.quotes.end ticks=%d", ticks)

StreamOpenedOrderProfits:

obs.Info("stream.pnl.start buf=%d", 1000)
if n%50 == 0 { obs.Info("stream.pnl.rate updates=%d", n) }
if err != nil { obs.Error("stream.pnl.error err=%v", err) }

Reconnect/backoff (Cookbook โ†’ HandleReconnect.md):

obs.Warn("reconnect reason=%v delay=%s attempt=%d", reason, delay, attempt)

โฑ๏ธ Unary RPCs โ€” latency & outcomes

Wrap calls with a tiny stopwatch.

t0 := time.Now()
q, err := s.ShowQuote(ctx, symbol)
dur := time.Since(t0)
if err != nil {
  obs.Error("rpc.show_quote symbol=%s dur=%s err=%v", symbol, dur, err)
} else {
  obs.Info("rpc.show_quote symbol=%s dur=%s digits=%d", symbol, dur, q.Digits)
}

Apply to: ShowQuotesMany, ShowSymbolParams, ShowTickValues, history getters, and order actions (ShowOrderSend/Modify/Close*).


๐Ÿงฎ Minimal metrics (standard lib)

No Prometheus? Start with processโ€‘local counters and periodic prints.

var (
  ticks uint64; pnlUpdates uint64; rpcOK uint64; rpcErr uint64
)
func incr(p *uint64) { atomic.AddUint64(p, 1) }

// In streams
incr(&ticks)
// In rpc success/fail
incr(&rpcOK); incr(&rpcErr)

// Reporter goroutine
func StartMetricsLogger() {
  go func(){
    t := time.NewTicker(10 * time.Second)
    for range t.C {
      ok := atomic.LoadUint64(&rpcOK)
      er := atomic.LoadUint64(&rpcErr)
      tk := atomic.LoadUint64(&ticks)
      obs.Info("metrics rpc_ok=%d rpc_err=%d ticks=%d", ok, er, tk)
    }
  }()
}

Call StartMetricsLogger() once in main.go.

Why it helps: you instantly see rates and error spikes without extra tooling.


๐Ÿ“Š (Optional) Prometheus exporter

If you later add infra, expose counters via promhttp.

var (
  mRpcOK  = prometheus.NewCounter(prometheus.CounterOpts{Name: "gomt4_rpc_ok"})
  mRpcErr = prometheus.NewCounter(prometheus.CounterOpts{Name: "gomt4_rpc_err"})
  mTicks  = prometheus.NewCounter(prometheus.CounterOpts{Name: "gomt4_ticks_total"})
)
func init(){ prometheus.MustRegister(mRpcOK, mRpcErr, mTicks) }
// http.ListenAndServe(":2112", promhttp.Handler())

Keep sampling in logs even with Prometheus for quick local debugging.


๐Ÿงต Sampling & log volume

  • Sample hot events (e.g., every 50th tick; or log once per symbol per second).
  • Group repetitive warnings (e.g., backoff bursts) with attempt counters.
  • Bound log file size if redirecting to disk (PowerShell: Start-Transcript or use a rotating writer).

๐Ÿ“ Code map (repo anchors)

  • examples/mt4/MT4_service.go โ†’ replace fmt.Println in:

  • StreamQuotes, StreamOpenedOrderProfits, StreamOpenedOrderTickets, StreamTradeUpdates

  • ShowQuote, ShowQuotesMany, ShowQuoteHistory
  • ShowOrderSendExample, ShowOrderModifyExample, ShowOrderCloseExample
  • examples/main.go โ†’ start metrics reporter early and print final summary on exit.

โœ… Checklist

  • Log start/end of each stream; sample midโ€‘flow ticks.
  • Log RPC latency and result (ok/err) with symbol/ticket context.
  • Keep secrets redacted.
  • Add 10โ€‘second metrics reporter (counters) in main.go.
  • Backoff logs include reason + delay + attempt.

See also

  • Performance Notes โ€” hot paths & batching
  • Security & Secrets โ€” redaction helper
  • Cookbook / Reliability โ€” HandleReconnect, UnaryRetries