Skip to content
This repository was archived by the owner on Jul 31, 2023. It is now read-only.

Commit 295a4b8

Browse files
committed
Add log exporter. (#1126)
* Add log exporter. * close log files when program terminates. * split stats into multiple lines. * fix review comments. * one more comment.
1 parent 0ac3701 commit 295a4b8

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed

examples/exporter/logexporter.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Copyright 2019, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package exporter contains a log exporter that supports exporting
16+
// OpenCensus metrics and spans to a logging framework.
17+
package exporter // import "go.opencensus.io/examples/exporter"
18+
19+
import (
20+
"context"
21+
"encoding/hex"
22+
"fmt"
23+
"log"
24+
"os"
25+
"sync"
26+
"time"
27+
28+
"go.opencensus.io/metric/metricdata"
29+
"go.opencensus.io/metric/metricexport"
30+
"go.opencensus.io/trace"
31+
)
32+
33+
// LogExporter exports metrics and span to log file
34+
type LogExporter struct {
35+
reader *metricexport.Reader
36+
ir *metricexport.IntervalReader
37+
initReaderOnce sync.Once
38+
o Options
39+
tFile *os.File
40+
mFile *os.File
41+
tLogger *log.Logger
42+
mLogger *log.Logger
43+
}
44+
45+
// Options provides options for LogExporter
46+
type Options struct {
47+
// ReportingInterval is a time interval between two successive metrics
48+
// export.
49+
ReportingInterval time.Duration
50+
51+
// MetricsLogFile is path where exported metrics are logged.
52+
// If it is nil then the metrics are logged on console
53+
MetricsLogFile string
54+
55+
// TracesLogFile is path where exported span data are logged.
56+
// If it is nil then the span data are logged on console
57+
TracesLogFile string
58+
}
59+
60+
func getLogger(filepath string) (*log.Logger, *os.File, error) {
61+
if filepath == "" {
62+
return log.New(os.Stdout, "", 0), nil, nil
63+
}
64+
f, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
65+
if err != nil {
66+
return nil, nil, err
67+
}
68+
return log.New(f, "", 0), f, nil
69+
}
70+
71+
// NewLogExporter creates new log exporter.
72+
func NewLogExporter(options Options) (*LogExporter, error) {
73+
e := &LogExporter{reader: metricexport.NewReader(),
74+
o: options}
75+
var err error
76+
e.tLogger, e.tFile, err = getLogger(options.TracesLogFile)
77+
if err != nil {
78+
return nil, err
79+
}
80+
e.mLogger, e.mFile, err = getLogger(options.MetricsLogFile)
81+
if err != nil {
82+
return nil, err
83+
}
84+
return e, nil
85+
}
86+
87+
func printMetricDescriptor(metric *metricdata.Metric) string {
88+
d := metric.Descriptor
89+
return fmt.Sprintf("name: %s, type: %s, unit: %s ",
90+
d.Name, d.Type, d.Unit)
91+
}
92+
93+
func printLabels(metric *metricdata.Metric, values []metricdata.LabelValue) string {
94+
d := metric.Descriptor
95+
kv := []string{}
96+
for i, k := range d.LabelKeys {
97+
kv = append(kv, fmt.Sprintf("%s=%v", k, values[i]))
98+
}
99+
return fmt.Sprintf("%v", kv)
100+
}
101+
102+
func printPoint(point metricdata.Point) string {
103+
switch v := point.Value.(type) {
104+
case *metricdata.Distribution:
105+
dv := v
106+
return fmt.Sprintf("count=%v sum=%v sum_sq_dev=%v, buckets=%v", dv.Count,
107+
dv.Sum, dv.SumOfSquaredDeviation, dv.Buckets)
108+
default:
109+
return fmt.Sprintf("value=%v", point.Value)
110+
}
111+
}
112+
113+
// Start starts the metric and span data exporter.
114+
func (e *LogExporter) Start() error {
115+
trace.RegisterExporter(e)
116+
e.initReaderOnce.Do(func() {
117+
e.ir, _ = metricexport.NewIntervalReader(&metricexport.Reader{}, e)
118+
})
119+
e.ir.ReportingInterval = e.o.ReportingInterval
120+
return e.ir.Start()
121+
}
122+
123+
// Stop stops the metric and span data exporter.
124+
func (e *LogExporter) Stop() {
125+
trace.UnregisterExporter(e)
126+
e.ir.Stop()
127+
}
128+
129+
// Close closes any files that were opened for logging.
130+
func (e *LogExporter) Close() {
131+
if e.tFile != nil {
132+
e.tFile.Close()
133+
e.tFile = nil
134+
}
135+
if e.mFile != nil {
136+
e.mFile.Close()
137+
e.mFile = nil
138+
}
139+
}
140+
141+
// ExportMetrics exports to log.
142+
func (e *LogExporter) ExportMetrics(ctx context.Context, metrics []*metricdata.Metric) error {
143+
for _, metric := range metrics {
144+
for _, ts := range metric.TimeSeries {
145+
for _, point := range ts.Points {
146+
e.mLogger.Println("#----------------------------------------------")
147+
e.mLogger.Println()
148+
e.mLogger.Printf("Metric: %s\n Labels: %s\n Value : %s\n",
149+
printMetricDescriptor(metric),
150+
printLabels(metric, ts.LabelValues),
151+
printPoint(point))
152+
e.mLogger.Println()
153+
}
154+
}
155+
}
156+
return nil
157+
}
158+
159+
// ExportSpan exports a SpanData to log
160+
func (e *LogExporter) ExportSpan(sd *trace.SpanData) {
161+
var (
162+
traceID = hex.EncodeToString(sd.SpanContext.TraceID[:])
163+
spanID = hex.EncodeToString(sd.SpanContext.SpanID[:])
164+
parentSpanID = hex.EncodeToString(sd.ParentSpanID[:])
165+
)
166+
e.tLogger.Println()
167+
e.tLogger.Println("#----------------------------------------------")
168+
e.tLogger.Println()
169+
e.tLogger.Println("TraceID: ", traceID)
170+
e.tLogger.Println("SpanID: ", spanID)
171+
if !reZero.MatchString(parentSpanID) {
172+
e.tLogger.Println("ParentSpanID:", parentSpanID)
173+
}
174+
175+
e.tLogger.Println()
176+
e.tLogger.Printf("Span: %v\n", sd.Name)
177+
e.tLogger.Printf("Status: %v [%v]\n", sd.Status.Message, sd.Status.Code)
178+
e.tLogger.Printf("Elapsed: %v\n", sd.EndTime.Sub(sd.StartTime).Round(time.Millisecond))
179+
180+
if len(sd.Annotations) > 0 {
181+
e.tLogger.Println()
182+
e.tLogger.Println("Annotations:")
183+
for _, item := range sd.Annotations {
184+
e.tLogger.Print(indent, item.Message)
185+
for k, v := range item.Attributes {
186+
e.tLogger.Printf(" %v=%v", k, v)
187+
}
188+
e.tLogger.Println()
189+
}
190+
}
191+
192+
if len(sd.Attributes) > 0 {
193+
e.tLogger.Println()
194+
e.tLogger.Println("Attributes:")
195+
for k, v := range sd.Attributes {
196+
e.tLogger.Printf("%v- %v=%v\n", indent, k, v)
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)