forked from HdrHistogram/hdrhistogram-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
log_writer.go
141 lines (126 loc) · 6.11 KB
/
log_writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//The log format encodes into a single file, multiple histograms with optional shared meta data.
package hdrhistogram
import (
"fmt"
"io"
"regexp"
"time"
)
const HISTOGRAM_LOG_FORMAT_VERSION = "1.3"
const MsToNsRatio float64 = 1000000.0
type HistogramLogOptions struct {
startTimeStampSec float64
endTimeStampSec float64
maxValueUnitRatio float64
}
func DefaultHistogramLogOptions() *HistogramLogOptions {
return &HistogramLogOptions{0, 0, MsToNsRatio}
}
type HistogramLogWriter struct {
baseTime int64
log io.Writer
}
// Return the current base time offset
func (lw *HistogramLogWriter) BaseTime() int64 {
return lw.baseTime
}
// Set a base time to subtract from supplied histogram start/end timestamps when
// logging based on histogram timestamps.
// baseTime is expected to be in msec since the epoch, as histogram start/end times
// are typically stamped with absolute times in msec since the epoch.
func (lw *HistogramLogWriter) SetBaseTime(baseTime int64) {
lw.baseTime = baseTime
}
func NewHistogramLogWriter(log io.Writer) *HistogramLogWriter {
return &HistogramLogWriter{baseTime: 0, log: log}
}
// Output an interval histogram, using the start/end timestamp indicated in the histogram, and the [optional] tag associated with the histogram.
// The histogram start and end timestamps are assumed to be in msec units
//
// By convention, histogram start/end time are generally stamped with absolute times in msec
// since the epoch. For logging with absolute time stamps, the base time would remain zero ( default ).
// For logging with relative time stamps (time since a start point), the base time should be set with SetBaseTime(baseTime int64)
//
// The max value in the histogram will be reported scaled down by a default maxValueUnitRatio of 1000000.0 (which is the msec : nsec ratio).
// If you need to specify a different start/end timestamp or a different maxValueUnitRatio you should use OutputIntervalHistogramWithLogOptions(histogram *Histogram, logOptions *HistogramLogOptions)
func (lw *HistogramLogWriter) OutputIntervalHistogram(histogram *Histogram) (err error) {
return lw.OutputIntervalHistogramWithLogOptions(histogram, nil)
}
// Output an interval histogram, with the given timestamp information and the [optional] tag associated with the histogram
//
// If you specify non-nil logOptions, and non-zero start timestamp, the the specified timestamp information will be used, and the start timestamp information in the actual histogram will be ignored.
// If you specify non-nil logOptions, and non-zero start timestamp, the the specified timestamp information will be used, and the end timestamp information in the actual histogram will be ignored.
// If you specify non-nil logOptions, The max value reported with the interval line will be scaled by the given maxValueUnitRatio,
// otherwise a default maxValueUnitRatio of 1,000,000 (which is the msec : nsec ratio) will be used.
//
// By convention, histogram start/end time are generally stamped with absolute times in msec
// since the epoch. For logging with absolute time stamps, the base time would remain zero ( default ).
// For logging with relative time stamps (time since a start point), the base time should be set with SetBaseTime(baseTime int64)
func (lw *HistogramLogWriter) OutputIntervalHistogramWithLogOptions(histogram *Histogram, logOptions *HistogramLogOptions) (err error) {
tag := histogram.Tag()
var match bool
tagStr := ""
if tag != "" {
match, err = regexp.MatchString(".[, \\r\\n].", tag)
if err != nil {
return
}
if match {
err = fmt.Errorf("Tag string cannot contain commas, spaces, or line breaks. Used tag: %s", tag)
return
}
tagStr = fmt.Sprintf("Tag=%s,", tag)
}
var usedStartTime float64 = float64(histogram.StartTimeMs())
var usedEndTime float64 = float64(histogram.EndTimeMs())
var maxValueUnitRatio float64 = MsToNsRatio
if logOptions != nil {
if logOptions.startTimeStampSec != 0 {
usedStartTime = logOptions.startTimeStampSec
}
if logOptions.endTimeStampSec != 0 {
usedEndTime = logOptions.endTimeStampSec
}
maxValueUnitRatio = logOptions.maxValueUnitRatio
}
startTime := usedStartTime - float64(lw.baseTime)/1000.0
endTime := usedEndTime - float64(lw.baseTime)/1000.0
maxValueAsDouble := float64(histogram.Max()) / maxValueUnitRatio
cpayload, err := histogram.Encode(V2CompressedEncodingCookieBase)
if err != nil {
return
}
_, err = lw.log.Write([]byte(fmt.Sprintf("%s%f,%f,%f,%s\n", tagStr, startTime, endTime, maxValueAsDouble, string(cpayload))))
return
}
// Log a start time in the log.
// Start time is represented as seconds since epoch with up to 3 decimal places. Line starts with the leading text '#[StartTime:'
func (lw *HistogramLogWriter) OutputStartTime(start_time_msec int64) (err error) {
secs := start_time_msec / 1000
iso_str := time.Unix(secs, start_time_msec%int64(1000)*int64(1000000000)).Format(time.RFC3339)
_, err = lw.log.Write([]byte(fmt.Sprintf("#[StartTime: %d (seconds since epoch), %s]\n", secs, iso_str)))
return
}
// Log a base time in the log.
// Base time is represented as seconds since epoch with up to 3 decimal places. Line starts with the leading text '#[BaseTime:'
func (lw *HistogramLogWriter) OutputBaseTime(base_time_msec int64) (err error) {
secs := base_time_msec / 1000
_, err = lw.log.Write([]byte(fmt.Sprintf("#[Basetime: %d (seconds since epoch)]\n", secs)))
return
}
// Log a comment to the log.
// A comment is any line that leads with '#' that is not matched by the BaseTime or StartTime formats. Comments are ignored when parsed.
func (lw *HistogramLogWriter) OutputComment(comment string) (err error) {
_, err = lw.log.Write([]byte(fmt.Sprintf("#%s\n", comment)))
return
}
// Output a legend line to the log.
// Human readable column headers. Ignored when parsed.
func (lw *HistogramLogWriter) OutputLegend() (err error) {
_, err = lw.log.Write([]byte("\"StartTimestamp\",\"Interval_Length\",\"Interval_Max\",\"Interval_Compressed_Histogram\"\n"))
return
}
// Output a log format version to the log.
func (lw *HistogramLogWriter) OutputLogFormatVersion() (err error) {
return lw.OutputComment(fmt.Sprintf("[Histogram log format version %s]", HISTOGRAM_LOG_FORMAT_VERSION))
}