This repository has been archived by the owner on Aug 30, 2019. It is now read-only.
/
main.go
179 lines (155 loc) · 4.78 KB
/
main.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package main
import (
"context"
"fmt"
"math/rand"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"strings"
"syscall"
"time"
_ "net/http/pprof"
log "github.com/cihub/seelog"
"github.com/DataDog/datadog-agent/pkg/pidfile"
"github.com/DataDog/datadog-trace-agent/internal/config"
"github.com/DataDog/datadog-trace-agent/internal/flags"
"github.com/DataDog/datadog-trace-agent/internal/info"
"github.com/DataDog/datadog-trace-agent/internal/metrics"
"github.com/DataDog/datadog-trace-agent/internal/osutil"
"github.com/DataDog/datadog-trace-agent/internal/watchdog"
)
// handleSignal closes a channel to exit cleanly from routines
func handleSignal(onSignal func()) {
sigChan := make(chan os.Signal, 10)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGPIPE)
for signo := range sigChan {
switch signo {
case syscall.SIGINT, syscall.SIGTERM:
log.Infof("received signal %d (%v)", signo, signo)
onSignal()
return
case syscall.SIGPIPE:
// By default systemd redirects the stdout to journald. When journald is stopped or crashes we receive a SIGPIPE signal.
// Go ignores SIGPIPE signals unless it is when stdout or stdout is closed, in this case the agent is stopped.
// We never want the agent to stop upon receiving SIGPIPE, so we intercept the SIGPIPE signals and just discard them.
default:
log.Warnf("unhandled signal %d (%v)", signo, signo)
}
}
}
const agentDisabledMessage = `trace-agent not enabled.
Set env var DD_APM_ENABLED=true or add
apm_enabled: true
to your datadog.conf file.
Exiting.`
// runAgent is the entrypoint of our code
func runAgent(ctx context.Context) {
// configure a default logger before anything so we can observe initialization
if flags.Info || flags.Version {
log.UseLogger(log.Disabled)
} else {
SetupDefaultLogger()
defer log.Flush()
}
defer watchdog.LogOnPanic()
// start CPU profiling
if flags.CPUProfile != "" {
f, err := os.Create(flags.CPUProfile)
if err != nil {
log.Critical(err)
}
pprof.StartCPUProfile(f)
log.Info("CPU profiling started...")
defer pprof.StopCPUProfile()
}
if flags.Version {
fmt.Print(info.VersionString())
return
}
if !flags.Info && flags.PIDFilePath != "" {
err := pidfile.WritePID(flags.PIDFilePath)
if err != nil {
log.Errorf("Error while writing PID file, exiting: %v", err)
os.Exit(1)
}
log.Infof("pid '%d' written to pid file '%s'", os.Getpid(), flags.PIDFilePath)
defer func() {
// remove pidfile if set
os.Remove(flags.PIDFilePath)
}()
}
cfg, err := config.Load(flags.ConfigPath)
if err != nil {
osutil.Exitf("%v", err)
}
err = info.InitInfo(cfg) // for expvar & -info option
if err != nil {
panic(err)
}
if flags.Info {
if err := info.Info(os.Stdout, cfg); err != nil {
os.Stdout.WriteString(fmt.Sprintf("failed to print info: %s\n", err))
os.Exit(1)
}
return
}
// Exit if tracing is not enabled
if !cfg.Enabled {
log.Info(agentDisabledMessage)
// a sleep is necessary to ensure that supervisor registers this process as "STARTED"
// If the exit is "too quick", we enter a BACKOFF->FATAL loop even though this is an expected exit
// http://supervisord.org/subprocess.html#process-states
time.Sleep(5 * time.Second)
return
}
// Initialize logging (replacing the default logger). No need
// to defer log.Flush, it was already done when calling
// "SetupDefaultLogger" earlier.
cfgLogLevel := strings.ToLower(cfg.LogLevel)
if cfgLogLevel == "warning" {
// to match core agent:
// https://github.com/DataDog/datadog-agent/blob/6f2d901aeb19f0c0a4e09f149c7cc5a084d2f708/pkg/config/log.go#L74-L76
cfgLogLevel = "warn"
}
logLevel, ok := log.LogLevelFromString(cfgLogLevel)
if !ok {
logLevel = log.InfoLvl
}
duration := 10 * time.Second
if !cfg.LogThrottlingEnabled {
duration = 0
}
err = SetupLogger(logLevel, cfg.LogFilePath, duration, 10)
if err != nil {
osutil.Exitf("cannot create logger: %v", err)
}
// Initialize dogstatsd client
err = metrics.Configure(cfg, []string{"version:" + info.Version})
if err != nil {
osutil.Exitf("cannot configure dogstatsd: %v", err)
}
// count the number of times the agent started
metrics.Count("datadog.trace_agent.started", 1, nil, 1)
// Seed rand
rand.Seed(time.Now().UTC().UnixNano())
agent := NewAgent(ctx, cfg)
log.Infof("trace-agent running on host %s", cfg.Hostname)
agent.Run()
// collect memory profile
if flags.MemProfile != "" {
f, err := os.Create(flags.MemProfile)
if err != nil {
log.Critical("could not create memory profile: ", err)
}
// get up-to-date statistics
runtime.GC()
// Not using WriteHeapProfile but instead calling WriteTo to
// make sure we pass debug=1 and resolve pointers to names.
if err := pprof.Lookup("heap").WriteTo(f, 1); err != nil {
log.Critical("could not write memory profile: ", err)
}
f.Close()
}
}