Skip to content

Commit cf08093

Browse files
committedJun 28, 2017
Streamline commandline arguments and config file options. Merged heroiclabs#68
Rename ops to dashboard.
1 parent 93c360a commit cf08093

12 files changed

+993
-169
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [keep a changelog](http://keepachangelog.com/) and this p
99

1010
### Changed
1111
- Run Facebook friends import after registration completes.
12+
- Streamline command line flags to be inline with the config file.
1213

1314
### Changed
1415
- Restructure and stabilize API messages.

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ $> nakama
2929
{"level":"info","ts":"$$timestamp$$","msg":"Data directory","path":"$$datadir$$"}
3030
{"level":"info","ts":"$$timestamp$$","msg":"Database connections","dsns":["root@localhost:26257"]}
3131
{"level":"info","ts":"$$timestamp$$","msg":"Evaluating modules","count":0,"modules":[]}
32-
{"level":"info","ts":"$$timestamp$$","msg":"Ops","port":7351}
32+
{"level":"info","ts":"$$timestamp$$","msg":"Dashboard","port":7351}
3333
{"level":"info","ts":"$$timestamp$$","msg":"Dashboard","url":"http://127.0.0.1:7351"}
3434
{"level":"info","ts":"$$timestamp$$","msg":"Client","port":7350}
3535
```

‎main.go

+8-78
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ package main
1616

1717
import (
1818
"database/sql"
19-
"flag"
2019
"fmt"
2120
"io/ioutil"
2221
"net/http"
@@ -34,9 +33,8 @@ import (
3433
"nakama/pkg/social"
3534

3635
"github.com/armon/go-metrics"
37-
"github.com/go-yaml/yaml"
3836
_ "github.com/lib/pq"
39-
uuid "github.com/satori/go.uuid"
37+
"github.com/satori/go.uuid"
4038
"go.uber.org/zap"
4139
)
4240

@@ -54,8 +52,7 @@ func main() {
5452
semver := fmt.Sprintf("%s+%s", version, commitID)
5553
http.DefaultClient.Timeout = 1500 * time.Millisecond // Always set default timeout on HTTP client
5654

57-
consoleLogger := server.NewJSONLogger(os.Stdout) // or NewConsoleLogger
58-
55+
cmdLogger := server.NewJSONLogger(os.Stdout, true) // or NewConsoleLogger
5956
if len(os.Args) > 1 {
6057
switch os.Args[1] {
6158
case "--version":
@@ -64,25 +61,19 @@ func main() {
6461
case "doctor":
6562
cmd.DoctorParse(os.Args[2:])
6663
case "migrate":
67-
cmd.MigrateParse(os.Args[2:], consoleLogger)
64+
cmd.MigrateParse(os.Args[2:], cmdLogger)
6865
case "admin":
69-
cmd.AdminParse(os.Args[2:], consoleLogger)
66+
cmd.AdminParse(os.Args[2:], cmdLogger)
7067
}
7168
}
7269

73-
config := parseArgs(consoleLogger)
70+
config := server.ParseArgs(cmdLogger, os.Args)
71+
jsonLogger, multiLogger := server.SetupLogging(config)
7472

7573
memoryMetricSink := metrics.NewInmemSink(10*time.Second, time.Minute)
7674
metric := &metrics.FanoutSink{memoryMetricSink}
7775
metrics.NewGlobal(&metrics.Config{EnableRuntimeMetrics: true, ProfileInterval: 5 * time.Second}, metric)
7876

79-
jsonLogger := server.NewLogger(consoleLogger, config)
80-
multiLogger := consoleLogger
81-
if !server.StdoutLogging {
82-
// if we aren't printing only to stdout, then we want to multiplex entries
83-
multiLogger = server.NewMultiLogger(consoleLogger, jsonLogger)
84-
}
85-
8677
// Print startup information
8778
multiLogger.Info("Nakama starting")
8879
multiLogger.Info("Node", zap.String("name", config.GetName()), zap.String("version", semver))
@@ -110,7 +101,7 @@ func main() {
110101
socialClient := social.NewClient(5 * time.Second)
111102
pipeline := server.NewPipeline(config, db, trackerService, matchmakerService, messageRouter, sessionRegistry, socialClient, runtime)
112103
authService := server.NewAuthenticationService(jsonLogger, config, db, statsService, sessionRegistry, socialClient, pipeline, runtime)
113-
opsService := server.NewOpsService(jsonLogger, multiLogger, semver, config, statsService)
104+
dashboardService := server.NewDashboardService(jsonLogger, multiLogger, semver, config, statsService)
114105

115106
gaenabled := len(os.Getenv("NAKAMA_TELEMETRY")) < 1
116107
cookie := newOrLoadCookie(config.GetDataDir())
@@ -128,7 +119,7 @@ func main() {
128119
multiLogger.Info("Shutting down")
129120

130121
authService.Stop()
131-
opsService.Stop()
122+
dashboardService.Stop()
132123
trackerService.Stop()
133124
runtime.Stop()
134125

@@ -145,67 +136,6 @@ func main() {
145136
select {}
146137
}
147138

148-
func parseArgs(consoleLogger *zap.Logger) server.Config {
149-
config := server.NewConfig()
150-
151-
flags := flag.NewFlagSet("main", flag.ExitOnError)
152-
flags.BoolVar(&server.VerboseLogging, "verbose", false, "Turn verbose logging on.")
153-
flags.BoolVar(&server.StdoutLogging, "logtostdout", false, "Log to stdout instead of file.")
154-
var configPath string
155-
flags.StringVar(&configPath, "config", "", "The absolute file path to configuration YAML file.")
156-
var name string
157-
flags.StringVar(&name, "name", "", "The virtual name of this server.")
158-
var datadir string
159-
flags.StringVar(&datadir, "data-dir", "", "The data directory to store server logs.")
160-
var dsn string
161-
flags.StringVar(&dsn, "db", "", "The database connection DSN. (default root@127.0.0.1:26257)")
162-
var port int
163-
flags.IntVar(&port, "port", -1, "Set port for client connections; all other ports will also be set sequentially.")
164-
var opsPort int
165-
flags.IntVar(&opsPort, "ops-port", -1, "Set port for ops dashboard.")
166-
167-
if err := flags.Parse(os.Args[1:]); err != nil {
168-
consoleLogger.Error("Could not parse command line arguments - ignoring command-line overrides", zap.Error(err))
169-
} else {
170-
171-
if len(configPath) > 0 {
172-
data, err := ioutil.ReadFile(configPath)
173-
if err != nil {
174-
consoleLogger.Error("Could not read config file, using defaults", zap.Error(err))
175-
} else {
176-
err = yaml.Unmarshal(data, config)
177-
if err != nil {
178-
consoleLogger.Error("Could not parse config file, using defaults", zap.Error(err))
179-
}
180-
}
181-
}
182-
183-
if len(name) > 0 {
184-
config.Name = name
185-
}
186-
if len(datadir) > 0 {
187-
config.Datadir = datadir
188-
}
189-
if len(dsn) > 0 {
190-
config.Dsns = []string{dsn}
191-
}
192-
if port != -1 {
193-
config.Port = port
194-
config.OpsPort = port + 1
195-
}
196-
if opsPort != -1 {
197-
config.OpsPort = opsPort
198-
}
199-
}
200-
201-
// if the runtime path is not overridden, set it to `datadir/modules`
202-
if config.GetRuntime().Path == "" {
203-
config.GetRuntime().Path = filepath.Join(config.GetDataDir(), "modules")
204-
}
205-
206-
return config
207-
}
208-
209139
func dbConnect(multiLogger *zap.Logger, dsns []string) *sql.DB {
210140
// TODO config database pooling
211141
rawurl := fmt.Sprintf("postgresql://%s?sslmode=disable", dsns[0])

‎pkg/flags/flags.go

+494
Large diffs are not rendered by default.

‎pkg/flags/vars.go

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
//
2+
// Permission is hereby granted, free of charge, to any person obtaining a copy
3+
// of this software and associated documentation files (the "Software"), to deal
4+
// in the Software without restriction, including without limitation the rights
5+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
6+
// copies of the Software, and to permit persons to whom the Software is
7+
// furnished to do so, subject to the following conditions:
8+
//
9+
// The above copyright notice and this permission notice shall be included in
10+
// all copies or substantial portions of the Software.
11+
//
12+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
18+
// THE SOFTWARE.
19+
20+
package flags
21+
22+
import (
23+
"fmt"
24+
"strconv"
25+
)
26+
27+
// additional types
28+
type int8Value int8
29+
type int16Value int16
30+
type int32Value int32
31+
type f32Value float32
32+
type uint8Value uint8
33+
type uint32Value uint32
34+
type uint16Value uint16
35+
36+
// Var handlers for each of the types
37+
func newInt8Value(p *int8) *int8Value {
38+
return (*int8Value)(p)
39+
}
40+
41+
func newInt16Value(p *int16) *int16Value {
42+
return (*int16Value)(p)
43+
}
44+
45+
func newInt32Value(p *int32) *int32Value {
46+
return (*int32Value)(p)
47+
}
48+
49+
func newFloat32Value(p *float32) *f32Value {
50+
return (*f32Value)(p)
51+
}
52+
53+
func newUint8Value(p *uint8) *uint8Value {
54+
return (*uint8Value)(p)
55+
}
56+
57+
func newUint16Value(p *uint16) *uint16Value {
58+
return (*uint16Value)(p)
59+
}
60+
61+
func newUint32Value(p *uint32) *uint32Value {
62+
return (*uint32Value)(p)
63+
}
64+
65+
// Setters for each of the types
66+
func (f *int8Value) Set(s string) error {
67+
v, err := strconv.ParseInt(s, 10, 8)
68+
if err != nil {
69+
return err
70+
}
71+
*f = int8Value(v)
72+
return nil
73+
}
74+
75+
func (f *int16Value) Set(s string) error {
76+
v, err := strconv.ParseInt(s, 10, 16)
77+
if err != nil {
78+
return err
79+
}
80+
*f = int16Value(v)
81+
return nil
82+
}
83+
84+
func (f *int32Value) Set(s string) error {
85+
v, err := strconv.ParseInt(s, 10, 32)
86+
if err != nil {
87+
return err
88+
}
89+
*f = int32Value(v)
90+
return nil
91+
}
92+
93+
func (f *f32Value) Set(s string) error {
94+
v, err := strconv.ParseFloat(s, 32)
95+
if err != nil {
96+
return err
97+
}
98+
*f = f32Value(v)
99+
return nil
100+
}
101+
102+
func (f *uint8Value) Set(s string) error {
103+
v, err := strconv.ParseUint(s, 10, 8)
104+
if err != nil {
105+
return err
106+
}
107+
*f = uint8Value(v)
108+
return nil
109+
}
110+
111+
func (f *uint16Value) Set(s string) error {
112+
v, err := strconv.ParseUint(s, 10, 16)
113+
if err != nil {
114+
return err
115+
}
116+
*f = uint16Value(v)
117+
return nil
118+
}
119+
120+
func (f *uint32Value) Set(s string) error {
121+
v, err := strconv.ParseUint(s, 10, 32)
122+
if err != nil {
123+
return err
124+
}
125+
*f = uint32Value(v)
126+
return nil
127+
}
128+
129+
// Getters for each of the types
130+
func (f *int8Value) Get() interface{} { return int8(*f) }
131+
func (f *int16Value) Get() interface{} { return int16(*f) }
132+
func (f *int32Value) Get() interface{} { return int32(*f) }
133+
func (f *f32Value) Get() interface{} { return float32(*f) }
134+
func (f *uint8Value) Get() interface{} { return uint8(*f) }
135+
func (f *uint16Value) Get() interface{} { return uint16(*f) }
136+
func (f *uint32Value) Get() interface{} { return uint32(*f) }
137+
138+
// Stringers for each of the types
139+
func (f *int8Value) String() string { return fmt.Sprintf("%v", *f) }
140+
func (f *int16Value) String() string { return fmt.Sprintf("%v", *f) }
141+
func (f *int32Value) String() string { return fmt.Sprintf("%v", *f) }
142+
func (f *f32Value) String() string { return fmt.Sprintf("%v", *f) }
143+
func (f *uint8Value) String() string { return fmt.Sprintf("%v", *f) }
144+
func (f *uint16Value) String() string { return fmt.Sprintf("%v", *f) }
145+
func (f *uint32Value) String() string { return fmt.Sprintf("%v", *f) }
146+
147+
// string slice
148+
149+
type strSlice struct {
150+
s []string
151+
set bool // if there a flag defined via command line, the slice will be cleared first.
152+
}
153+
154+
func newStringSlice(p []string) *strSlice {
155+
return &strSlice{
156+
s: p,
157+
set: false,
158+
}
159+
}
160+
161+
func (s *strSlice) Set(str string) error {
162+
if !s.set {
163+
s.s = (s.s)[:0]
164+
s.set = true
165+
}
166+
s.s = append(s.s, str)
167+
return nil
168+
}
169+
170+
func (s *strSlice) Get() interface{} {
171+
return []string(s.s)
172+
}
173+
174+
func (s *strSlice) String() string {
175+
return fmt.Sprintf("%v", s.s)
176+
}
177+
178+
// int slice
179+
type intSlice struct {
180+
s []int
181+
set bool
182+
}
183+
184+
func newIntSlice(p []int) *intSlice {
185+
return &intSlice{
186+
s: p,
187+
set: false,
188+
}
189+
}
190+
191+
func (is *intSlice) Set(str string) error {
192+
i, err := strconv.Atoi(str)
193+
if err != nil {
194+
return err
195+
}
196+
if !is.set {
197+
is.s = (is.s)[:0]
198+
is.set = true
199+
}
200+
is.s = append(is.s, i)
201+
return nil
202+
}
203+
204+
func (is *intSlice) Get() interface{} {
205+
return []int(is.s)
206+
}
207+
208+
func (is *intSlice) String() string {
209+
return fmt.Sprintf("%v", is.s)
210+
}
211+
212+
// float64 slice
213+
type float64Slice struct {
214+
s []float64
215+
set bool
216+
}
217+
218+
func newFloat64Slice(p []float64) *float64Slice {
219+
return &float64Slice{
220+
s: p,
221+
set: false,
222+
}
223+
}
224+
225+
func (is *float64Slice) Set(str string) error {
226+
i, err := strconv.ParseFloat(str, 64)
227+
if err != nil {
228+
return err
229+
}
230+
if !is.set {
231+
is.s = (is.s)[:0]
232+
is.set = true
233+
}
234+
is.s = append(is.s, i)
235+
return nil
236+
}
237+
238+
func (is *float64Slice) Get() interface{} {
239+
return []float64(is.s)
240+
}
241+
242+
func (is *float64Slice) String() string {
243+
return fmt.Sprintf("%v", is.s)
244+
}

‎server/config.go

+112-39
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,84 @@ import (
1919
"path/filepath"
2020
"strings"
2121

22+
"flag"
23+
"io/ioutil"
24+
"nakama/pkg/flags"
25+
26+
"github.com/go-yaml/yaml"
2227
"github.com/satori/go.uuid"
28+
"go.uber.org/zap"
2329
)
2430

2531
// Config interface is the Nakama Core configuration
2632
type Config interface {
2733
GetName() string
2834
GetDataDir() string
2935
GetPort() int
30-
GetOpsPort() int
36+
GetDashboardPort() int
3137
GetDSNS() []string
38+
GetLog() *LogConfig
3239
GetSession() *SessionConfig
3340
GetTransport() *TransportConfig
3441
GetDatabase() *DatabaseConfig
3542
GetSocial() *SocialConfig
3643
GetRuntime() *RuntimeConfig
3744
}
3845

46+
func ParseArgs(logger *zap.Logger, args []string) Config {
47+
config := NewConfig()
48+
49+
if len(args) > 1 {
50+
switch args[1] {
51+
case "--config":
52+
configPath := args[2]
53+
data, err := ioutil.ReadFile(configPath)
54+
if err != nil {
55+
logger.Error("Could not read config file, using defaults", zap.Error(err))
56+
} else {
57+
err = yaml.Unmarshal(data, config)
58+
if err != nil {
59+
logger.Error("Could not parse config file, using defaults", zap.Error(err))
60+
} else {
61+
config.Config = configPath
62+
}
63+
}
64+
}
65+
}
66+
67+
flagSet := flag.NewFlagSet("nakama", flag.ExitOnError)
68+
fm := flags.NewFlagMakerFlagSet(&flags.FlagMakingOptions{
69+
UseLowerCase: true,
70+
Flatten: false,
71+
TagName: "yaml",
72+
TagUsage: "usage",
73+
}, flagSet)
74+
75+
if _, err := fm.ParseArgs(config, args[1:]); err != nil {
76+
logger.Error("Could not parse command line arguments - ignoring command-line overrides", zap.Error(err))
77+
}
78+
79+
// if the runtime path is not overridden, set it to `datadir/modules`
80+
if config.GetRuntime().Path == "" {
81+
config.GetRuntime().Path = filepath.Join(config.GetDataDir(), "modules")
82+
}
83+
84+
return config
85+
}
86+
3987
type config struct {
40-
Name string `yaml:"name" json:"name"`
41-
Datadir string `yaml:"data_dir" json:"data_dir"`
42-
Port int `yaml:"port" json:"port"`
43-
OpsPort int `yaml:"ops_port" json:"ops_port"`
44-
Dsns []string `yaml:"dsns" json:"dsns"`
45-
Session *SessionConfig `yaml:"session" json:"session"`
46-
Transport *TransportConfig `yaml:"transport" json:"transport"`
47-
Database *DatabaseConfig `yaml:"database" json:"database"`
48-
Social *SocialConfig `yaml:"social" json:"social"`
49-
Runtime *RuntimeConfig `yaml:"runtime" json:"runtime"`
88+
Name string `yaml:"name" json:"name" usage:"Nakama server’s node name - must be unique"`
89+
Config string `yaml:"config" json:"config" usage:"The absolute file path to configuration YAML file."`
90+
Datadir string `yaml:"data_dir" json:"data_dir" usage:"An absolute path to a writeable folder where Nakama will store its data."`
91+
Port int `yaml:"port" json:"port" usage:"The port for accepting connections from the client, listening on all interfaces. Unless explicitly defined, other ports will be chosen sequentially from here upwards."`
92+
DashboardPort int `yaml:"dashboard_port" json:"dashboard_port" usage:"The port for accepting connections to the dashboard, listening on all interfaces."`
93+
Dsns []string `yaml:"dsns" json:"dsns" usage:"List of fully qualified JDBC addresses of CockroachDB servers."`
94+
Log *LogConfig `yaml:"log" json:"log" usage:"Log levels and output"`
95+
Session *SessionConfig `yaml:"session" json:"session" usage:"Session authentication settings"`
96+
Transport *TransportConfig `yaml:"transport" json:"transport" usage:"Data transport configurations"`
97+
Database *DatabaseConfig `yaml:"database" json:"database" usage:"Database connection settings"`
98+
Social *SocialConfig `yaml:"social" json:"social" usage:"Properties for social providers"`
99+
Runtime *RuntimeConfig `yaml:"runtime" json:"runtime" usage:"Script Runtime properties"`
50100
}
51101

52102
// NewConfig constructs a Config struct which represents server settings.
@@ -55,16 +105,16 @@ func NewConfig() *config {
55105
dataDirectory := filepath.Join(cwd, "data")
56106
nodeName := "nakama-" + strings.Split(uuid.NewV4().String(), "-")[3]
57107
return &config{
58-
Name: nodeName,
59-
Datadir: dataDirectory,
60-
Port: 7350,
61-
OpsPort: 7351,
62-
Dsns: []string{"root@localhost:26257"},
63-
Session: NewSessionConfig(),
64-
Transport: NewTransportConfig(),
65-
Database: NewDatabaseConfig(),
66-
Social: NewSocialConfig(),
67-
Runtime: NewRuntimeConfig(),
108+
Name: nodeName,
109+
Datadir: dataDirectory,
110+
Port: 7350,
111+
DashboardPort: 7351,
112+
Dsns: []string{"root@localhost:26257"},
113+
Session: NewSessionConfig(),
114+
Transport: NewTransportConfig(),
115+
Database: NewDatabaseConfig(),
116+
Social: NewSocialConfig(),
117+
Runtime: NewRuntimeConfig(),
68118
}
69119
}
70120

@@ -80,14 +130,18 @@ func (c *config) GetPort() int {
80130
return c.Port
81131
}
82132

83-
func (c *config) GetOpsPort() int {
84-
return c.OpsPort
133+
func (c *config) GetDashboardPort() int {
134+
return c.DashboardPort
85135
}
86136

87137
func (c *config) GetDSNS() []string {
88138
return c.Dsns
89139
}
90140

141+
func (c *config) GetLog() *LogConfig {
142+
return c.Log
143+
}
144+
91145
func (c *config) GetSession() *SessionConfig {
92146
return c.Session
93147
}
@@ -108,10 +162,29 @@ func (c *config) GetRuntime() *RuntimeConfig {
108162
return c.Runtime
109163
}
110164

165+
// LogConfig is configuration relevant to logging levels and output
166+
type LogConfig struct {
167+
// By default, log all messages with Warn and Error messages to a log file inside Data/Log/<name>.log file. The content will be in JSON.
168+
// if --log.verbose is passed, log messages with Debug and higher levels.
169+
// if --log.stdout is passed, logs are only printed to stdout.
170+
// In all cases, Error messages trigger the stacktrace to be dumped as well.
171+
172+
Verbose bool `yaml:"verbose" json:"verbose" usage:"Turn verbose logging on"`
173+
Stdout bool `yaml:"stdout" json:"stdout" usage:"Log to stdout instead of file"`
174+
}
175+
176+
// NewLogConfig creates a new LogConfig struct
177+
func NewLogConfig() *LogConfig {
178+
return &LogConfig{
179+
Verbose: false,
180+
Stdout: false,
181+
}
182+
}
183+
111184
// SessionConfig is configuration relevant to the session
112185
type SessionConfig struct {
113-
EncryptionKey string `yaml:"encryption_key" json:"encryption_key"`
114-
TokenExpiryMs int64 `yaml:"token_expiry_ms" json:"token_expiry_ms"`
186+
EncryptionKey string `yaml:"encryption_key" json:"encryption_key" usage:"The encryption key used to produce the client token."`
187+
TokenExpiryMs int64 `yaml:"token_expiry_ms" json:"token_expiry_ms" usage:"Token expiry in milliseconds."`
115188
}
116189

117190
// NewSessionConfig creates a new SessionConfig struct
@@ -124,11 +197,11 @@ func NewSessionConfig() *SessionConfig {
124197

125198
// TransportConfig is configuration relevant to the transport socket and protocol
126199
type TransportConfig struct {
127-
ServerKey string `yaml:"server_key" json:"server_key"`
128-
MaxMessageSizeBytes int64 `yaml:"max_message_size_bytes" json:"max_message_size_bytes"`
129-
WriteWaitMs int `yaml:"write_wait_ms" json:"write_wait_ms"`
130-
PongWaitMs int `yaml:"pong_wait_ms" json:"pong_wait_ms"`
131-
PingPeriodMs int `yaml:"ping_period_ms" json:"ping_period_ms"`
200+
ServerKey string `yaml:"server_key" json:"server_key" usage:"Server key to use to establish a connection to the server."`
201+
MaxMessageSizeBytes int64 `yaml:"max_message_size_bytes" json:"max_message_size_bytes" usage:"Maximum amount of data in bytes allowed to be read from the client socket per message."`
202+
WriteWaitMs int `yaml:"write_wait_ms" json:"write_wait_ms" usage:"Time in milliseconds to wait for an ack from the client when writing data."`
203+
PongWaitMs int `yaml:"pong_wait_ms" json:"pong_wait_ms" usage:"Time in milliseconds to wait for a pong message from the client after sending a ping."`
204+
PingPeriodMs int `yaml:"ping_period_ms" json:"ping_period_ms" usage:"Time in milliseconds to wait between client ping messages. This value must be less than the pong_wait_ms."`
132205
}
133206

134207
// NewTransportConfig creates a new TransportConfig struct
@@ -144,9 +217,9 @@ func NewTransportConfig() *TransportConfig {
144217

145218
// DatabaseConfig is configuration relevant to the Database storage
146219
type DatabaseConfig struct {
147-
ConnMaxLifetimeMs int `yaml:"conn_max_lifetime_ms" json:"conn_max_lifetime_ms"`
148-
MaxOpenConns int `yaml:"max_open_conns" json:"max_open_conns"`
149-
MaxIdleConns int `yaml:"max_idle_conns" json:"max_idle_conns"`
220+
ConnMaxLifetimeMs int `yaml:"conn_max_lifetime_ms" json:"conn_max_lifetime_ms" usage:"Time in milliseconds to reuse a database connection before the connection is killed and a new one is created."`
221+
MaxOpenConns int `yaml:"max_open_conns" json:"max_open_conns" usage:"Maximum number of allowed open connections to the database."`
222+
MaxIdleConns int `yaml:"max_idle_conns" json:"max_idle_conns" usage:"Maximum number of allowed open but unused connections to the database."`
150223
}
151224

152225
// NewDatabaseConfig creates a new DatabaseConfig struct
@@ -160,13 +233,13 @@ func NewDatabaseConfig() *DatabaseConfig {
160233

161234
// SocialConfig is configuration relevant to the Social providers
162235
type SocialConfig struct {
163-
Steam *SocialConfigSteam `yaml:"steam" json:"steam"`
236+
Steam *SocialConfigSteam `yaml:"steam" json:"steam" usage:"Steam configuration"`
164237
}
165238

166239
// SocialConfigSteam is configuration relevant to Steam
167240
type SocialConfigSteam struct {
168-
PublisherKey string `yaml:"publisher_key" json:"publisher_key"`
169-
AppID int `yaml:"app_id" json:"app_id"`
241+
PublisherKey string `yaml:"publisher_key" json:"publisher_key" usage:"Steam Publisher Key value."`
242+
AppID int `yaml:"app_id" json:"app_id" usage:"Steam App ID."`
170243
}
171244

172245
// NewSocialConfig creates a new SocialConfig struct
@@ -181,9 +254,9 @@ func NewSocialConfig() *SocialConfig {
181254

182255
// RuntimeConfig is configuration relevant to the Runtime Lua VM
183256
type RuntimeConfig struct {
184-
Environment map[string]interface{} `yaml:"env" json:"env"`
185-
Path string `yaml:"path" json:"path"`
186-
HTTPKey string `yaml:"http_key" json:"http_key"`
257+
Environment map[string]interface{} `yaml:"env" json:"env"` // not supported in FlagOverrides
258+
Path string `yaml:"path" json:"path" usage:"Path of modules for the server to scan."`
259+
HTTPKey string `yaml:"http_key" json:"http_key" usage:"Runtime HTTP Invocation key"`
187260
}
188261

189262
// NewRuntimeConfig creates a new RuntimeConfig struct

‎server/ops_accepter.go ‎server/dashboard_accepter.go

+12-13
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import (
2929
"go.uber.org/zap"
3030
)
3131

32-
// opsService is responsible for serving the dashboard and all of its required resources
33-
type opsService struct {
32+
// DashboardService is responsible for serving the dashboard and all of its required resources
33+
type dashboardService struct {
3434
logger *zap.Logger
3535
version string
3636
config Config
@@ -39,9 +39,9 @@ type opsService struct {
3939
dashboardFilesystem http.FileSystem
4040
}
4141

42-
// NewOpsService creates a new opsService
43-
func NewOpsService(logger *zap.Logger, multiLogger *zap.Logger, version string, config Config, statsService StatsService) *opsService {
44-
service := &opsService{
42+
// NewDashboardService creates a new dashboardService
43+
func NewDashboardService(logger *zap.Logger, multiLogger *zap.Logger, version string, config Config, statsService StatsService) *dashboardService {
44+
service := &dashboardService{
4545
logger: logger,
4646
version: version,
4747
config: config,
@@ -60,41 +60,40 @@ func NewOpsService(logger *zap.Logger, multiLogger *zap.Logger, version string,
6060
service.mux.PathPrefix("/").Handler(http.FileServer(service.dashboardFilesystem)).Methods("GET") //needs to be last
6161

6262
go func() {
63-
bindAddr := fmt.Sprintf(":%d", config.GetOpsPort())
63+
bindAddr := fmt.Sprintf(":%d", config.GetDashboardPort())
6464
handlerWithCORS := handlers.CORS(handlers.AllowedOrigins([]string{"*"}))(service.mux)
6565
err := http.ListenAndServe(bindAddr, handlerWithCORS)
6666
if err != nil {
67-
multiLogger.Fatal("Ops listener failed", zap.Error(err))
67+
multiLogger.Fatal("Dashboard listener failed", zap.Error(err))
6868
}
6969
}()
70-
multiLogger.Info("Ops", zap.Int("port", config.GetOpsPort()))
7170
hostname, err := os.Hostname()
7271
if err != nil {
7372
hostname = "127.0.0.1"
7473
}
75-
multiLogger.Info("Dashboard", zap.String("url", fmt.Sprintf("http://%s:%d", hostname, config.GetOpsPort())))
74+
multiLogger.Info("Dashboard", zap.String("address", fmt.Sprintf("http://%s:%d", hostname, config.GetDashboardPort())))
7675

7776
return service
7877
}
7978

80-
func (s *opsService) Stop() {}
79+
func (s *dashboardService) Stop() {}
8180

82-
func (s *opsService) statusHandler(w http.ResponseWriter, r *http.Request) {
81+
func (s *dashboardService) statusHandler(w http.ResponseWriter, r *http.Request) {
8382
w.Header().Set("Content-Type", "application/json; charset=utf-8")
8483

8584
stats := s.statsService.GetStats()
8685
statsJSON, _ := json.Marshal(stats)
8786
w.Write(statsJSON)
8887
}
8988

90-
func (s *opsService) configHandler(w http.ResponseWriter, r *http.Request) {
89+
func (s *dashboardService) configHandler(w http.ResponseWriter, r *http.Request) {
9190
w.Header().Set("Content-Type", "application/json; charset=utf-8")
9291

9392
config, _ := json.Marshal(s.config)
9493
w.Write(config)
9594
}
9695

97-
func (s *opsService) infoHandler(w http.ResponseWriter, r *http.Request) {
96+
func (s *dashboardService) infoHandler(w http.ResponseWriter, r *http.Request) {
9897
w.Header().Set("Content-Type", "application/json; charset=utf-8")
9998

10099
info := map[string]interface{}{

‎server/log.go

+25-18
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,19 @@ import (
2424
"go.uber.org/zap/zapcore"
2525
)
2626

27-
// By default, log all messages with Warn and Error messages to a log file inside Data/Log/<name>.log file. The content will be in JSON.
28-
// if --verbose is passed, log messages with Debug and higher levels.
29-
// if --logtostdout is passed, logs are only printed to stdout.
30-
// In all cases, Error messages trigger the stacktrace to be dumped as well.
31-
var (
32-
VerboseLogging = true
33-
StdoutLogging = false
34-
)
35-
36-
type loggerEnabler struct{}
27+
type loggerEnabler struct {
28+
verbose bool
29+
}
3730

3831
func (l *loggerEnabler) Enabled(level zapcore.Level) bool {
39-
return VerboseLogging || level > zapcore.DebugLevel
32+
return l.verbose || level > zapcore.DebugLevel
4033
}
4134

42-
func NewLogger(consoleLogger *zap.Logger, config Config) *zap.Logger {
35+
func NewLogger(config Config) *zap.Logger {
36+
consoleLogger := NewJSONLogger(os.Stdout, true)
37+
4338
output := os.Stdout
44-
if !StdoutLogging {
39+
if !config.GetLog().Stdout {
4540
err := os.MkdirAll(filepath.FromSlash(config.GetDataDir()+"/log"), 0755)
4641
if err != nil {
4742
consoleLogger.Fatal("Could not create log directory", zap.Error(err))
@@ -55,13 +50,13 @@ func NewLogger(consoleLogger *zap.Logger, config Config) *zap.Logger {
5550
}
5651
}
5752

58-
logger := NewJSONLogger(output)
53+
logger := NewJSONLogger(output, config.GetLog().Verbose)
5954
logger = logger.With(zap.String("server", config.GetName()))
6055

6156
return logger
6257
}
6358

64-
func NewConsoleLogger(output *os.File) *zap.Logger {
59+
func NewConsoleLogger(output *os.File, verbose bool) *zap.Logger {
6560
consoleEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
6661
TimeKey: "ts",
6762
LevelKey: "level",
@@ -75,13 +70,13 @@ func NewConsoleLogger(output *os.File) *zap.Logger {
7570
EncodeCaller: zapcore.ShortCallerEncoder,
7671
})
7772

78-
core := zapcore.NewCore(consoleEncoder, output, &loggerEnabler{})
73+
core := zapcore.NewCore(consoleEncoder, output, &loggerEnabler{verbose})
7974
options := []zap.Option{zap.AddStacktrace(zap.ErrorLevel)}
8075

8176
return zap.New(core, options...)
8277
}
8378

84-
func NewJSONLogger(output *os.File) *zap.Logger {
79+
func NewJSONLogger(output *os.File, verbose bool) *zap.Logger {
8580
jsonEncoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
8681
TimeKey: "ts",
8782
LevelKey: "level",
@@ -95,7 +90,7 @@ func NewJSONLogger(output *os.File) *zap.Logger {
9590
EncodeCaller: zapcore.ShortCallerEncoder,
9691
})
9792

98-
core := zapcore.NewCore(jsonEncoder, output, &loggerEnabler{})
93+
core := zapcore.NewCore(jsonEncoder, output, &loggerEnabler{verbose})
9994
options := []zap.Option{zap.AddStacktrace(zap.ErrorLevel)}
10095

10196
return zap.New(core, options...)
@@ -111,3 +106,15 @@ func NewMultiLogger(loggers ...*zap.Logger) *zap.Logger {
111106
options := []zap.Option{zap.AddStacktrace(zap.ErrorLevel)}
112107
return zap.New(teeCore, options...)
113108
}
109+
110+
func SetupLogging(config Config) (*zap.Logger, *zap.Logger) {
111+
consoleLogger := NewJSONLogger(os.Stdout, config.GetLog().Verbose)
112+
jsonLogger := NewLogger(config)
113+
multiLogger := consoleLogger
114+
if !config.GetLog().Stdout {
115+
// if we aren't printing only to stdout, then we want to multiplex entries
116+
multiLogger = NewMultiLogger(consoleLogger, jsonLogger)
117+
}
118+
119+
return jsonLogger, multiLogger
120+
}
File renamed without changes.

‎tests/config_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package tests
2+
3+
import (
4+
"nakama/server"
5+
"os"
6+
"testing"
7+
)
8+
9+
const CONFIG_FILE = "config_test.yml"
10+
11+
var l = server.NewConsoleLogger(os.Stdout, true)
12+
13+
func TestConfigLoad(t *testing.T) {
14+
c := server.ParseArgs(l, []string{"nakama", "--config", CONFIG_FILE})
15+
16+
if c.GetName() != "nakama-test" {
17+
t.Error("Unmatched config value - name")
18+
}
19+
if c.GetRuntime().HTTPKey != "testkey" {
20+
t.Error("Unmatched config value - runtime.http_key")
21+
}
22+
}
23+
24+
func TestConfigLoadOverride(t *testing.T) {
25+
c := server.ParseArgs(l, []string{
26+
"nakama",
27+
"--config",
28+
CONFIG_FILE,
29+
"--log.stdout",
30+
"--runtime.http_key",
31+
"testkey-override",
32+
})
33+
34+
if c.GetName() != "nakama-test" {
35+
t.Error("Unmatched config value - name")
36+
}
37+
38+
if !c.GetLog().Stdout {
39+
t.Error("Unmatched config value - log.stdout")
40+
}
41+
42+
if c.GetRuntime().HTTPKey != "testkey-override" {
43+
t.Error("Unmatched config value - name")
44+
}
45+
}
46+
47+
func TestCmdOverride(t *testing.T) {
48+
c := server.ParseArgs(l, []string{
49+
"nakama",
50+
"--name",
51+
"nakama-test-override",
52+
"--log.stdout",
53+
"--runtime.http_key",
54+
"testkey-override",
55+
})
56+
57+
if c.GetName() != "nakama-test-override" {
58+
t.Error("Unmatched config value - name")
59+
}
60+
61+
if !c.GetLog().Stdout {
62+
t.Error("Unmatched config value - log.stdout")
63+
}
64+
65+
if c.GetRuntime().HTTPKey != "testkey-override" {
66+
t.Error("Unmatched config value - runtime.http_key")
67+
}
68+
}

‎tests/config_test.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: nakama-test
2+
dsns:
3+
- test@localhost:26257
4+
log:
5+
verbose: true
6+
stdout: false
7+
runtime:
8+
http_key: testkey

‎tests/runtime_test.go

+20-20
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func newRuntime() (*server.Runtime, error) {
4848
}
4949

5050
func writeStatsModule() {
51-
writeFile("stats.lua", `
51+
writeLuaModule("stats.lua", `
5252
stats={}
5353
5454
-- Get the mean value of a table
@@ -70,7 +70,7 @@ return stats`)
7070
}
7171

7272
func writeTestModule() {
73-
writeFile("test.lua", `
73+
writeLuaModule("test.lua", `
7474
test={}
7575
-- Get the mean value of a table
7676
function test.printWorld()
@@ -83,7 +83,7 @@ return test
8383
`)
8484
}
8585

86-
func writeFile(name, content string) {
86+
func writeLuaModule(name, content string) {
8787
os.MkdirAll(filepath.Join(DATA_PATH, "modules"), os.ModePerm)
8888
ioutil.WriteFile(filepath.Join(DATA_PATH, "/modules/"+name), []byte(content), 0644)
8989
}
@@ -137,7 +137,7 @@ file_exists "./"`)
137137
func TestRuntimeRequireEval(t *testing.T) {
138138
defer os.RemoveAll(DATA_PATH)
139139
writeTestModule()
140-
writeFile("test-invoke.lua", `
140+
writeLuaModule("test-invoke.lua", `
141141
local nakama = require("nakama")
142142
local test = require("test")
143143
test.printWorld()
@@ -153,7 +153,7 @@ test.printWorld()
153153
func TestRuntimeRequireFile(t *testing.T) {
154154
defer os.RemoveAll(DATA_PATH)
155155
writeStatsModule()
156-
writeFile("local_test.lua", `
156+
writeLuaModule("local_test.lua", `
157157
local stats = require("stats")
158158
t = {[1]=5, [2]=7, [3]=8, [4]='Something else.'}
159159
assert(stats.mean(t) > 0)
@@ -169,7 +169,7 @@ assert(stats.mean(t) > 0)
169169
func TestRuntimeRequirePreload(t *testing.T) {
170170
defer os.RemoveAll(DATA_PATH)
171171
writeStatsModule()
172-
writeFile("states-invoke.lua", `
172+
writeLuaModule("states-invoke.lua", `
173173
local stats = require("stats")
174174
t = {[1]=5, [2]=7, [3]=8, [4]='Something else.'}
175175
print(stats.mean(t))
@@ -184,7 +184,7 @@ print(stats.mean(t))
184184

185185
func TestRuntimeKeepChangesBetweenStates(t *testing.T) {
186186
defer os.RemoveAll(DATA_PATH)
187-
writeFile("var.lua", `
187+
writeLuaModule("var.lua", `
188188
var={}
189189
var.count = 1
190190
return var
@@ -229,7 +229,7 @@ assert(var.count == 2)`)
229229
func TestRuntimeRegisterHTTP(t *testing.T) {
230230
defer os.RemoveAll(DATA_PATH)
231231
writeTestModule()
232-
writeFile("http-invoke.lua", `
232+
writeLuaModule("http-invoke.lua", `
233233
local nakama = require("nakama")
234234
local test = require("test")
235235
nakama.register_http(test.printWorld, "test/helloworld")
@@ -255,7 +255,7 @@ nakama.register_http(test.printWorld, "test/helloworld")
255255

256256
func TestRuntimeRegisterHTTPNoResponse(t *testing.T) {
257257
defer os.RemoveAll(DATA_PATH)
258-
writeFile("test.lua", `
258+
writeLuaModule("test.lua", `
259259
test={}
260260
-- Get the mean value of a table
261261
function test.printWorld(ctx)
@@ -265,7 +265,7 @@ end
265265
print("Test Module Loaded")
266266
return test
267267
`)
268-
writeFile("http-invoke.lua", `
268+
writeLuaModule("http-invoke.lua", `
269269
local nakama = require("nakama")
270270
local test = require("test")
271271
nakama.register_http(test.printWorld, "test/helloworld")
@@ -286,7 +286,7 @@ nakama.register_http(test.printWorld, "test/helloworld")
286286

287287
func TestRuntimeRegisterHTTPWithPayload(t *testing.T) {
288288
defer os.RemoveAll(DATA_PATH)
289-
writeFile("test.lua", `
289+
writeLuaModule("test.lua", `
290290
test={}
291291
-- Get the mean value of a table
292292
function test.printWorld(ctx, payload)
@@ -298,7 +298,7 @@ end
298298
print("Test Module Loaded")
299299
return test
300300
`)
301-
writeFile("http-invoke.lua", `
301+
writeLuaModule("http-invoke.lua", `
302302
local nakama = require("nakama")
303303
local test = require("test")
304304
nakama.register_http(test.printWorld, "test/helloworld")
@@ -327,7 +327,7 @@ nakama.register_http(test.printWorld, "test/helloworld")
327327

328328
func TestRuntimeRegisterRPCWithPayload(t *testing.T) {
329329
defer os.RemoveAll(DATA_PATH)
330-
writeFile("test.lua", `
330+
writeLuaModule("test.lua", `
331331
test={}
332332
-- Get the mean value of a table
333333
function test.printWorld(ctx, payload)
@@ -339,7 +339,7 @@ end
339339
print("Test Module Loaded")
340340
return test
341341
`)
342-
writeFile("http-invoke.lua", `
342+
writeLuaModule("http-invoke.lua", `
343343
local nakama = require("nakama")
344344
local test = require("test")
345345
nakama.register_rpc(test.printWorld, "helloworld")
@@ -366,7 +366,7 @@ nakama.register_rpc(test.printWorld, "helloworld")
366366

367367
func TestRuntimeRegisterBeforeWithPayload(t *testing.T) {
368368
defer os.RemoveAll(DATA_PATH)
369-
writeFile("test.lua", `
369+
writeLuaModule("test.lua", `
370370
test={}
371371
-- Get the mean value of a table
372372
function test.printWorld(ctx, payload)
@@ -378,7 +378,7 @@ end
378378
print("Test Module Loaded")
379379
return test
380380
`)
381-
writeFile("http-invoke.lua", `
381+
writeLuaModule("http-invoke.lua", `
382382
local nakama = require("nakama")
383383
local test = require("test")
384384
nakama.register_before(test.printWorld, "SelfFetch")
@@ -440,7 +440,7 @@ nakama.register_before(test.printWorld, "SelfFetch")
440440

441441
func TestRuntimeUserId(t *testing.T) {
442442
defer os.RemoveAll(DATA_PATH)
443-
writeFile("userid.lua", `
443+
writeLuaModule("userid.lua", `
444444
local nk = require("nakama")
445445
446446
local user_ids = {
@@ -461,7 +461,7 @@ local users = nk.user_fetch_id(user_ids)
461461

462462
func TestRuntimeLeaderboardCreate(t *testing.T) {
463463
defer os.RemoveAll(DATA_PATH)
464-
writeFile("userid.lua", `
464+
writeLuaModule("userid.lua", `
465465
local nk = require("nakama")
466466
local nkx = require("nakamax")
467467
@@ -483,7 +483,7 @@ nk.leaderboard_create(leaderboard_id, "desc", "0 0 * * 1", metadata, false)
483483

484484
func TestStorageWrite(t *testing.T) {
485485
defer os.RemoveAll(DATA_PATH)
486-
writeFile("storage_write.lua", `
486+
writeLuaModule("storage_write.lua", `
487487
local nk = require("nakama")
488488
489489
local new_records = {
@@ -505,7 +505,7 @@ nk.storage_write(new_records)
505505

506506
func TestStorageFetch(t *testing.T) {
507507
defer os.RemoveAll(DATA_PATH)
508-
writeFile("storage_fetch.lua", `
508+
writeLuaModule("storage_fetch.lua", `
509509
local nk = require("nakama")
510510
local record_keys = {
511511
{Bucket = "mygame", Collection = "settings", Record = "a", UserId = nil},

0 commit comments

Comments
 (0)
Please sign in to comment.