Skip to content

Commit 6d64e51

Browse files
Send events in batch and with custom timestamp.
Enable to generate log events with custom timestamp not only "Now()". Also implement the POST request to the Splunk Server with batches of events to save http calls.
1 parent 7b0895a commit 6d64e51

File tree

1 file changed

+89
-30
lines changed

1 file changed

+89
-30
lines changed

splunk/splunk.go

Lines changed: 89 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
package splunk
22

33
import (
4-
"crypto/tls"
5-
"time"
64
"bytes"
7-
"os"
8-
"net/http"
5+
"crypto/tls"
96
"encoding/json"
107
"errors"
8+
"net/http"
9+
"os"
10+
"time"
1111
)
1212

1313
// Event represents the log event object that is sent to Splunk when Client.Log is called.
1414
type Event struct {
15-
Time int64 `json:"time" binding:"required"` // epoch time in seconds
16-
Host string `json:"host" binding:"required"` // hostname
17-
Source string `json:"source" binding:"required"` // app name
18-
SourceType string `json:"sourcetype" binding:"required"` // Splunk bucket to group logs in
19-
Index string `json:"index" binding:"required"` // idk what it does..
20-
Event interface{} `json:"event" binding:"required"` // throw any useful key/val pairs here
15+
Time int64 `json:"time" binding:"required"` // epoch time in seconds
16+
Host string `json:"host" binding:"required"` // hostname
17+
Source string `json:"source" binding:"required"` // app name
18+
SourceType string `json:"sourcetype" binding:"required"` // Splunk bucket to group logs in
19+
Index string `json:"index" binding:"required"` // idk what it does..
20+
Event interface{} `json:"event" binding:"required"` // throw any useful key/val pairs here
2121
}
2222

2323
// Client manages communication with Splunk's HTTP Event Collector.
@@ -27,37 +27,66 @@ type Event struct {
2727
// The Token field must be defined with your access token to the Event Collector.
2828
// The Source, SourceType, and Index fields must be defined.
2929
type Client struct {
30-
HTTPClient *http.Client // HTTP client used to communicate with the API
31-
URL string
32-
Token string
33-
Source string
34-
SourceType string
35-
Index string
30+
HTTPClient *http.Client // HTTP client used to communicate with the API
31+
URL string
32+
Hostname string
33+
Token string
34+
Source string //Default source
35+
SourceType string //Default source type
36+
Index string //Default index
3637
}
3738

3839
// NewClient creates a new client to Splunk.
3940
// This should be the primary way a Splunk client object is constructed.
4041
//
4142
// If an httpClient object is specified it will be used instead of the
4243
// default http.DefaultClient.
43-
func NewClient(httpClient *http.Client, URL string, Token string, Source string, SourceType string, Index string) (*Client) {
44+
func NewClient(httpClient *http.Client, URL string, Token string, Source string, SourceType string, Index string) *Client {
4445
// Create a new client
4546
if httpClient == nil {
4647
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} // turn off certificate checking
4748
httpClient = &http.Client{Timeout: time.Second * 20, Transport: tr}
4849
}
49-
50-
c := &Client{HTTPClient: httpClient, URL: URL, Token: Token, Source: Source, SourceType: SourceType, Index: Index}
51-
50+
hostname, _ := os.Hostname()
51+
c := &Client{
52+
HTTPClient: httpClient,
53+
URL: URL,
54+
Hostname: hostname,
55+
Token: Token,
56+
Source: Source,
57+
SourceType: SourceType,
58+
Index: Index,
59+
}
5260
return c
5361
}
5462

5563
// NewEvent creates a new log event to send to Splunk.
5664
// This should be the primary way a Splunk log object is constructed, and is automatically called by the Log function attached to the client.
57-
func NewEvent(event interface{}, source string, sourcetype string, index string) (Event) {
58-
hostname, _ := os.Hostname()
59-
e := Event{Time: time.Now().Unix(), Host: hostname, Source: source, SourceType: sourcetype, Index: index, Event: event}
65+
// This method takes the current timestamp for the event, meaning that the event is generated at runtime.
66+
func (c *Client) NewEvent(event interface{}, source string, sourcetype string, index string) *Event {
67+
e := &Event{
68+
Time: time.Now().Unix(),
69+
Host: c.Hostname,
70+
Source: source,
71+
SourceType: sourcetype,
72+
Index: index,
73+
Event: event,
74+
}
75+
return e
76+
}
6077

78+
// NewEventWithTime creates a new log event with a specified timetamp to send to Splunk.
79+
// This is similar to NewEvent but if you want to log in a different time rather than time.Now this becomes handy. If that's
80+
// the case, use this function to create the Event object and the the LogEvent function.
81+
func (c *Client) NewEventWithTime(t int64, event interface{}, source string, sourcetype string, index string) *Event {
82+
e := &Event{
83+
Time: t,
84+
Host: c.Hostname,
85+
Source: source,
86+
SourceType: sourcetype,
87+
Index: index,
88+
Event: event,
89+
}
6190
return e
6291
}
6392

@@ -66,23 +95,54 @@ func NewEvent(event interface{}, source string, sourcetype string, index string)
6695
// All that must be provided for a log event are the desired map[string]string key/val pairs. These can be anything
6796
// that provide context or information for the situation you are trying to log (i.e. err messages, status codes, etc).
6897
// The function auto-generates the event timestamp and hostname for you.
69-
func (c *Client) Log(event interface{}) (error) {
98+
func (c *Client) Log(event interface{}) error {
7099
// create Splunk log
71-
log := NewEvent(event, c.Source, c.SourceType, c.Index)
100+
log := c.NewEvent(event, c.Source, c.SourceType, c.Index)
101+
return c.LogEvent(log)
102+
}
72103

104+
// Client.LogWithTime is used to construct a new log event with a scpecified timestamp and POST it to the Splunk server.
105+
//
106+
// This is similar to Client.Log, just with the t parameter.
107+
func (c *Client) LogWithTime(t int64, event interface{}) error {
108+
// create Splunk log
109+
log := c.NewEventWithTime(t, event, c.Source, c.SourceType, c.Index)
110+
return c.LogEvent(log)
111+
}
112+
113+
// Client.LogEvent is used to POST a single event to the Splunk server.
114+
func (c *Client) LogEvent(e *Event) error {
73115
// Convert requestBody struct to byte slice to prep for http.NewRequest
74-
b, err := json.Marshal(log)
116+
b, err := json.Marshal(e)
75117
if err != nil {
76118
return err
77119
}
120+
return c.doRequest(bytes.NewBuffer(b))
121+
}
78122

79-
//log.Print(string(b[:])) // print what the splunk post body will be for checking/debugging
123+
// Client.LogEvents is used to POST multiple events with a single request to the Splunk server.
124+
func (c *Client) LogEvents(events []*Event) error {
125+
buf := new(bytes.Buffer)
126+
for _, e := range events {
127+
b, err := json.Marshal(e)
128+
if err != nil {
129+
return err
130+
}
131+
buf.Write(b)
132+
// Each json object should be separated by a blank line
133+
buf.WriteString("\r\n\r\n")
134+
}
135+
// Convert requestBody struct to byte slice to prep for http.NewRequest
136+
return c.doRequest(buf)
137+
}
80138

139+
// Client.doRequest is used internally to POST the bytes of events to the Splunk server.
140+
func (c *Client) doRequest(b *bytes.Buffer) error {
81141
// make new request
82142
url := c.URL
83-
req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
143+
req, err := http.NewRequest("POST", url, b)
84144
req.Header.Add("Content-Type", "application/json")
85-
req.Header.Add("Authorization", "Splunk " + c.Token)
145+
req.Header.Add("Authorization", "Splunk "+c.Token)
86146

87147
// receive response
88148
res, err := c.HTTPClient.Do(req)
@@ -102,6 +162,5 @@ func (c *Client) Log(event interface{}) (error) {
102162
err = errors.New(responseBody)
103163

104164
}
105-
//log.Print(responseBody) // print error to screen for checking/debugging
106165
return err
107166
}

0 commit comments

Comments
 (0)