├── COPYING ├── README.md └── newrelic └── newrelic.go /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2014 Paul Smith 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # New Relic Go Agent 2 | 3 | An unofficial [New Relic][nr] agent for the Go programming language. Allows your Go web 4 | app to appear as an application in New Relic (under the "APM" tab). 5 | 6 | It wraps the [New Relic Agent SDK][sdk] -- the SDK is currently Linux only, therefore, 7 | so is this Go agent. 8 | 9 | Features: 10 | 11 | * Transaction monitoring -- from top of a web request to bottom 12 | * Segments -- external calls, database calls, etc. 13 | * Custom metrics 14 | 15 | [nr]: https://newrelic.com/ 16 | [sdk]: https://docs.newrelic.com/docs/agents/agent-sdk/using-agent-sdk/getting-started-agent-sdk 17 | -------------------------------------------------------------------------------- /newrelic/newrelic.go: -------------------------------------------------------------------------------- 1 | // Go wrapper for the New Relic Agent SDK. Requires Linux and the SDK headers 2 | // and libraries. 3 | package newrelic 4 | 5 | /* 6 | #cgo LDFLAGS: -L/usr/local/lib -lnewrelic-collector-client -lnewrelic-common -lnewrelic-transaction 7 | #include "newrelic_collector_client.h" 8 | #include "newrelic_common.h" 9 | #include "newrelic_transaction.h" 10 | #include 11 | */ 12 | import "C" 13 | 14 | import ( 15 | "errors" 16 | "unsafe" 17 | ) 18 | 19 | var statusMap = map[int]string{ 20 | 0: "ok", 21 | -0x10001: "other", 22 | -0x20001: "disabled", 23 | -0x30001: "invalid param", 24 | -0x30002: "invalid id", 25 | -0x40001: "transaction not started", 26 | -0x40002: "transaction in progress", 27 | -0x40003: "transaction not named", 28 | } 29 | 30 | const ( 31 | AUTOSCOPE = 1 32 | ROOT_SEGMENT = 0 33 | ) 34 | 35 | func nrError(i C.int, name string) error { 36 | if int(i) < -1 { 37 | status, ok := statusMap[int(i)] 38 | if !ok { 39 | status = "unknown" 40 | } 41 | return errors.New("newrelic: " + name + ": " + status) 42 | } 43 | return nil 44 | } 45 | 46 | func Init(license string, appName string, lang string, langVersion string) error { 47 | C.newrelic_register_message_handler((*[0]byte)(C.newrelic_message_handler)) 48 | clicense := C.CString(license) 49 | defer C.free(unsafe.Pointer(clicense)) 50 | cappName := C.CString(appName) 51 | defer C.free(unsafe.Pointer(cappName)) 52 | clang := C.CString(lang) 53 | defer C.free(unsafe.Pointer(clang)) 54 | clangVersion := C.CString(langVersion) 55 | defer C.free(unsafe.Pointer(clangVersion)) 56 | rv := C.newrelic_init(clicense, cappName, clang, clangVersion) 57 | return nrError(rv, "initialize") 58 | } 59 | 60 | func RequestShutdown(reason string) error { 61 | ptr := C.CString(reason) 62 | defer C.free(unsafe.Pointer(ptr)) 63 | rv := C.newrelic_request_shutdown(ptr) 64 | return nrError(rv, "request shutdown") 65 | } 66 | 67 | func BeginTransaction() int64 { 68 | id := C.newrelic_transaction_begin() 69 | return int64(id) 70 | } 71 | 72 | func SetTransactionName(txnID int64, name string) error { 73 | cname := C.CString(name) 74 | defer C.free(unsafe.Pointer(cname)) 75 | rv := C.newrelic_transaction_set_name(C.long(txnID), cname) 76 | return nrError(rv, "set transaction name") 77 | } 78 | 79 | func BeginGenericSegment(txnID int64, parentID int64, name string) int64 { 80 | cname := C.CString(name) 81 | defer C.free(unsafe.Pointer(cname)) 82 | id := C.newrelic_segment_generic_begin(C.long(txnID), C.long(parentID), cname) 83 | return int64(id) 84 | } 85 | 86 | func BeginDatastoreSegment( 87 | txnID int64, 88 | parentID int64, 89 | table string, 90 | operation string, 91 | sql string, 92 | rollupName string, 93 | ) int64 { 94 | ctable := C.CString(table) 95 | defer C.free(unsafe.Pointer(ctable)) 96 | coperation := C.CString(operation) 97 | defer C.free(unsafe.Pointer(coperation)) 98 | csql := C.CString(sql) 99 | defer C.free(unsafe.Pointer(csql)) 100 | crollupName := C.CString(rollupName) 101 | defer C.free(unsafe.Pointer(crollupName)) 102 | id := C.newrelic_segment_datastore_begin( 103 | C.long(txnID), 104 | C.long(parentID), 105 | ctable, 106 | coperation, 107 | csql, 108 | crollupName, 109 | (*[0]byte)(C.newrelic_basic_literal_replacement_obfuscator), 110 | ) 111 | return int64(id) 112 | } 113 | 114 | func BeginExternalSegment(txnID int64, parentID int64, host string, name string) int64 { 115 | chost := C.CString(host) 116 | defer C.free(unsafe.Pointer(chost)) 117 | cname := C.CString(name) 118 | defer C.free(unsafe.Pointer(cname)) 119 | id := C.newrelic_segment_external_begin(C.long(txnID), C.long(parentID), chost, cname) 120 | return int64(id) 121 | } 122 | 123 | func EndSegment(txnID int64, parentID int64) error { 124 | rv := C.newrelic_segment_end(C.long(txnID), C.long(parentID)) 125 | return nrError(rv, "end segment") 126 | } 127 | 128 | func SetTransactionRequestURL(txnID int64, url string) error { 129 | curl := C.CString(url) 130 | defer C.free(unsafe.Pointer(curl)) 131 | rv := C.newrelic_transaction_set_request_url(C.long(txnID), curl) 132 | return nrError(rv, "set transaction request url") 133 | } 134 | 135 | func SetWebTransaction(txnID int64) error { 136 | rv := C.newrelic_transaction_set_type_web(C.long(txnID)) 137 | return nrError(rv, "set web transaction") 138 | } 139 | 140 | func EndTransaction(txnID int64) error { 141 | rv := C.newrelic_transaction_end(C.long(txnID)) 142 | return nrError(rv, "end transaction") 143 | } 144 | 145 | func RecordMetric(name string, val float64) error { 146 | cname := C.CString(name) 147 | defer C.free(unsafe.Pointer(cname)) 148 | rv := C.newrelic_record_metric(cname, C.double(val)) 149 | return nrError(rv, "record metric") 150 | } 151 | 152 | func NoticeError(txnID int64, typ string, msg string, stackTrace string, delim string) error { 153 | ctyp := C.CString(typ) 154 | defer C.free(unsafe.Pointer(ctyp)) 155 | cmsg := C.CString(msg) 156 | defer C.free(unsafe.Pointer(cmsg)) 157 | cstackTrace := C.CString(stackTrace) 158 | defer C.free(unsafe.Pointer(cstackTrace)) 159 | cdelim := C.CString(delim) 160 | defer C.free(unsafe.Pointer(cdelim)) 161 | rv := C.newrelic_transaction_notice_error(C.long(txnID), ctyp, cmsg, cstackTrace, cdelim) 162 | return nrError(rv, "notice error") 163 | } 164 | 165 | func AddAttribute(txnID int64, name string, value string) error { 166 | cname := C.CString(name) 167 | defer C.free(unsafe.Pointer(cname)) 168 | cvalue := C.CString(value) 169 | defer C.free(unsafe.Pointer(cvalue)) 170 | rv := C.newrelic_transaction_add_attribute(C.long(txnID), cname, cvalue) 171 | return nrError(rv, "add attribute") 172 | } 173 | --------------------------------------------------------------------------------