forked from ryandotsmith/l2met
/
main.go
146 lines (135 loc) · 3.27 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
// L2met converts a formatted log stream into metrics.
package main
import (
"flag"
"fmt"
"github.com/ryandotsmith/l2met/auth"
"github.com/ryandotsmith/l2met/conf"
"github.com/ryandotsmith/l2met/metchan"
"github.com/ryandotsmith/l2met/outlet"
"github.com/ryandotsmith/l2met/reader"
"github.com/ryandotsmith/l2met/receiver"
"github.com/ryandotsmith/l2met/store"
"io/ioutil"
"log"
"net/http"
"runtime"
"time"
)
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
}
// Hold onto the app's global config.
var cfg *conf.D
func main() {
cfg = conf.New()
flag.Parse()
// Can be passed to other modules
// as an internal metrics channel.
mchan := metchan.New(cfg.Verbose, cfg.MetchanUrl)
mchan.Start()
// The store will be used by receivers and outlets.
var st store.Store
if len(cfg.RedisHost) > 0 {
redisStore := store.NewRedisStore(cfg)
redisStore.Mchan = mchan
st = redisStore
fmt.Printf("at=initialized-redis-store\n")
} else {
st = store.NewMemStore()
fmt.Printf("at=initialized-mem-store\n")
}
if cfg.UseOutlet {
rdr := reader.New(cfg, st)
rdr.Mchan = mchan
outlet := outlet.NewLibratoOutlet(cfg, rdr)
outlet.Mchan = mchan
outlet.Start()
}
if cfg.UsingReciever {
recv := receiver.NewReceiver(cfg, st)
recv.Mchan = mchan
recv.Start()
if cfg.Verbose {
go recv.Report()
}
http.HandleFunc("/logs",
func(w http.ResponseWriter, r *http.Request) {
startReceiveT := time.Now()
if r.Method != "POST" {
http.Error(w, "Invalid Request", 400)
return
}
user, pass, err := auth.ParseAndDecrypt(r)
if err != nil {
http.Error(w, "Invalid Request", 400)
return
}
b, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
http.Error(w, "Invalid Request", 400)
return
}
v := r.URL.Query()
v.Add("user", user)
v.Add("password", pass)
recv.Receive(b, v)
mchan.Time("http.accept", startReceiveT)
})
}
// The only thing that constitutes a healthy l2met
// is the health of the store. In some cases, this might mean
// a Redis health check.
http.HandleFunc("/health",
func(w http.ResponseWriter, r *http.Request) {
ok := st.Health()
if !ok {
msg := "Store is unavailable."
fmt.Printf("error=%q\n", msg)
http.Error(w, msg, 500)
}
})
http.HandleFunc("/sign",
func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method must be POST.", 400)
return
}
l := r.Header.Get("Authorization")
user, _, err := auth.ParseRaw(l)
if err != nil {
http.Error(w, "Unable to parse headers.", 400)
return
}
matched := false
for i := range cfg.Secrets {
if user == cfg.Secrets[i] {
matched = true
break
}
}
if !matched {
http.Error(w, "Authentication failed.", 401)
return
}
b, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
http.Error(w, "Unable to read body.", 400)
return
}
signed, err := auth.EncryptAndSign(b)
if err != nil {
http.Error(w, "Unable to sign body.", 500)
return
}
fmt.Fprint(w, string(signed))
})
// Start the HTTP server.
e := http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), nil)
if e != nil {
log.Fatal("Unable to start HTTP server.")
}
fmt.Printf("at=l2met-initialized port=%d\n", cfg.Port)
}