-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathserver.go
207 lines (168 loc) · 5.34 KB
/
server.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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// Copyright 2016 struktur AG. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package phoenix
import (
"fmt"
"io"
// Provide pprof support via the default servemux.
_ "net/http/pprof"
"os"
"path"
goruntime "runtime"
"runtime/pprof"
)
// RunFunc is the completion callback for server setup.
type RunFunc func(Runtime) error
// Server provides pre-startup configuration and application boot functionality.
type Server interface {
// DefaultOption sets the default value of the named option in the given
// section.
DefaultOption(section, option, value string) Server
// OverrideOption forces the named option in the given section
// to have the given value regardless of it's state in the
// config file.
OverrideOption(section, option, value string) Server
// Config sets the path to the application's main config file.
Config(path *string) Server
// DefaultConfig sets the path to the application's default config file.
DefaultConfig(path *string) Server
// OverrideConfig sets the path to the application's override config file.
OverrideConfig(path *string) Server
// Log sets the path to the application's logfile. Defaults to stderr if unset.
Log(path *string) Server
// CpuProfile runs the application with CPU profiling enabled,
// writing the results to path.
CpuProfile(path *string) Server
// MemProfile runs the application with memory profiling enabled,
// writing the results to path.
MemProfile(path *string) Server
// Run initializes a Runtime instance and provides it to the runner callback,
// returning any errors produced by the callback.
//
// Any errors resulting from loading the configuration or opening the log
// will be returned without calling runner.
Run(runner RunFunc) error
// Stop forcibly halts the running instance.
Stop() error
}
type server struct {
Name, Version string
logPath *string
cpuProfile, memProfile *string
currentRuntime *runtime
*config
}
// NewServer creates a Server instance with the given name and version string.
func NewServer(name, version string) Server {
return &server{
Name: name,
Version: version,
config: newConfig(),
}
}
func (server *server) DefaultOption(section, name, value string) Server {
server.config.DefaultOption(section, name, value)
return server
}
func (server *server) OverrideOption(section, name, value string) Server {
server.config.OverrideOption(section, name, value)
return server
}
func (server *server) Config(path *string) Server {
server.config.SetPath(*path)
return server
}
func (server *server) DefaultConfig(path *string) Server {
server.config.SetDefaultPath(*path)
return server
}
func (server *server) OverrideConfig(path *string) Server {
server.config.SetOverridePath(*path)
return server
}
func (server *server) Log(path *string) Server {
server.logPath = path
return server
}
func (server *server) CpuProfile(path *string) Server {
server.cpuProfile = path
return server
}
func (server *server) MemProfile(path *string) Server {
server.memProfile = path
return server
}
func (server *server) Run(runFunc RunFunc) (err error) {
if server.currentRuntime != nil {
return fmt.Errorf("server is already running")
}
container, err := newContainer(server.Name, server.Version, server.logPath, server.config)
if err != nil {
makeLogger(server.Name, os.Stderr).Print(err)
return err
}
defer container.Close()
// Now that logging is started, install a panic handler.
defer func() {
if recovered := recover(); recovered != nil {
if panicedError, ok := recovered.(error); ok {
err = panicedError
} else {
err = fmt.Errorf("%v", recovered)
}
stackTrace := make([]byte, 1024)
for {
n := goruntime.Stack(stackTrace, false)
if n < len(stackTrace) {
stackTrace = stackTrace[0:n]
break
}
stackTrace = make([]byte, len(stackTrace)*2)
}
container.Printf("%v\n%s", err, stackTrace)
}
}()
runtime := newRuntime(container, runFunc)
if server.cpuProfile != nil && *server.cpuProfile != "" {
runtime.OnStart(func(runtime Runtime) error {
cpuprofilepath := path.Clean(*server.cpuProfile)
runtime.Printf("Writing CPU profile to %s", cpuprofilepath)
f, err := os.Create(cpuprofilepath)
if err != nil {
return fmt.Errorf("failed to open CPU profile: %v", err)
}
return pprof.StartCPUProfile(f)
})
runtime.OnStop(func(_ Runtime) {
pprof.StopCPUProfile()
})
}
if server.memProfile != nil && *server.memProfile != "" {
memprofilepath := path.Clean(*server.memProfile)
var profileData io.WriteCloser
runtime.OnStart(func(runtime Runtime) (err error) {
runtime.Printf("A memory profile will be written to %s on exit.", memprofilepath)
profileData, err = os.Create(memprofilepath)
return
})
runtime.OnStop(func(runtime Runtime) {
runtime.Printf("Writing memory profile to %s", memprofilepath)
defer profileData.Close()
if err := pprof.Lookup("heap").WriteTo(profileData, 0); err != nil {
runtime.Printf("Failed to create memory profile: %v", err)
}
})
}
server.currentRuntime = runtime
err = server.currentRuntime.Run()
return
}
func (server *server) Stop() error {
if server.currentRuntime == nil {
return fmt.Errorf("server is not currently running")
}
err := server.currentRuntime.Stop()
server.currentRuntime = nil
return err
}