github.com/mundipagg/tracer-splunk-writer@v1.0.6/splunk.go (about) 1 package splunk 2 3 import ( 4 "bytes" 5 "fmt" 6 "net/http" 7 "os" 8 "regexp" 9 "sync" 10 "time" 11 12 "strings" 13 14 jsoniter "github.com/json-iterator/go" 15 "github.com/mralves/tracer" 16 "github.com/mundipagg/tracer-splunk-writer/buffer" 17 "github.com/mundipagg/tracer-splunk-writer/json" 18 s "github.com/mundipagg/tracer-splunk-writer/strings" 19 ) 20 21 type Writer struct { 22 sync.Locker 23 address string 24 key string 25 configLineLog map[string]interface{} 26 defaultPropertiesSplunk map[string]interface{} 27 defaultPropertiesApp map[string]interface{} 28 client *http.Client 29 buffer buffer.Buffer 30 minimumLevel uint8 31 marshaller jsoniter.API 32 messageEnvelop string 33 } 34 35 var punctuation = regexp.MustCompile(`(.+?)[?;:\\.,!]?$`) 36 37 //Used when message contains properties to replace. 38 var r = strings.NewReplacer("{", "{{.", "}", "}}") 39 40 func (sw *Writer) Write(entry tracer.Entry) { 41 go func(sw *Writer, entry tracer.Entry) { 42 defer func() { 43 if err := recover(); err != nil { 44 stderr("COULD NOT SEND SPLUNK TO SPLUNK BECAUSE %v", err) 45 } 46 }() 47 if entry.Level > sw.minimumLevel { 48 return 49 } 50 51 extraProperties := map[string]interface{}{ 52 "RequestKey": entry.TransactionId, 53 } 54 55 if s.IsBlank(entry.TransactionId) { 56 delete(extraProperties, "RequestKey") 57 } 58 59 properties := NewEntry(append(entry.Args, sw.defaultPropertiesApp, extraProperties)) 60 61 message := punctuation.FindStringSubmatch(s.Capitalize(entry.Message))[1] 62 63 message, err := s.ProcessString(r.Replace(message), properties) 64 if err != nil { 65 message = entry.Message 66 } 67 68 l := NewEntry(sw.configLineLog) 69 l.Add("time", time.Now().UTC().UnixNano()) 70 e := NewEntry(Entry{ 71 "AdditionalData": properties, 72 "Message": message, 73 "Severity": Level(entry.Level), 74 }, sw.defaultPropertiesSplunk) 75 l.Add("event", e) 76 77 sw.buffer.Write(l) 78 }(sw, entry) 79 } 80 81 func (sw *Writer) send(events []interface{}) error { 82 defer func() { 83 err := recover() 84 if err != nil { 85 fmt.Printf("%v\n", err) 86 } 87 }() 88 89 body, err := sw.marshaller.Marshal(events) 90 if err != nil { 91 stderr("COULD NOT SEND LOG TO SPLUNK BECAUSE %v", err) 92 return err 93 } 94 95 request, _ := http.NewRequest(http.MethodPost, sw.address, bytes.NewBuffer(body)) 96 if len(sw.key) > 0 { 97 request.Header.Set("Authorization", "Splunk "+sw.key) 98 } 99 request.Header.Set("Content-Type", "application/json") 100 101 var response *http.Response 102 response, err = sw.client.Do(request) 103 if err != nil { 104 stderr("COULD NOT SEND LOG TO SPLUNK BECAUSE %v", err) 105 return err 106 } 107 response.Body.Close() 108 if response.StatusCode != 200 { 109 stderr("COULD NOT SEND LOG TO SPLUNK BECAUSE %v", response.Status) 110 return fmt.Errorf("request returned %v", response.StatusCode) 111 } 112 113 return nil 114 } 115 116 func stderr(message string, args ...interface{}) { 117 fmt.Fprintf(os.Stderr, message+"\n", args...) 118 } 119 120 type Config struct { 121 Address string 122 Key string 123 Application string 124 Buffer buffer.Config 125 MinimumLevel uint8 126 Timeout time.Duration 127 ConfigLineLog Entry 128 DefaultPropertiesSplunk Entry 129 DefaultPropertiesApp Entry 130 MessageEnvelop string 131 } 132 133 func New(config Config) *Writer { 134 writer := Writer{ 135 Locker: &sync.RWMutex{}, 136 address: config.Address, 137 key: config.Key, 138 client: &http.Client{ 139 Timeout: config.Timeout, 140 Transport: &http.Transport{ 141 TLSHandshakeTimeout: config.Timeout, 142 IdleConnTimeout: config.Timeout, 143 }, 144 }, 145 messageEnvelop: config.MessageEnvelop, 146 minimumLevel: config.MinimumLevel, 147 configLineLog: config.ConfigLineLog, 148 defaultPropertiesSplunk: config.DefaultPropertiesSplunk, 149 defaultPropertiesApp: config.DefaultPropertiesApp, 150 marshaller: json.NewWithCaseStrategy(s.UseAnnotation), 151 } 152 config.Buffer.OnOverflow = writer.send 153 writer.buffer = buffer.New(config.Buffer) 154 return &writer 155 }