-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Logicmonitorexporter implementation #14182
Changes from all commits
6eb6546
fb779f4
c448e02
0bacdc7
10dc4ab
1a47198
aff0572
3dfc422
e82e207
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' | ||
change_type: new_component | ||
|
||
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) | ||
component: logicmonitorexporter | ||
|
||
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). | ||
note: New exporter for exporting traces and logs to Logicmonitor Platform | ||
|
||
# One or more tracking issues related to the change | ||
issues: [13727] | ||
|
||
# (Optional) One or more lines of additional information to render under the primary note. | ||
# These lines will be padded with 2 spaces and then inserted directly into the document. | ||
# Use pipe (|) for multiline entries. | ||
subtext: |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include ../../Makefile.Common | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New line. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# LogicMonitor Exporter | ||
This exporter supports sending logs and traces data to [Logicmonitor](https://www.logicmonitor.com/). | ||
|
||
## Configuration Options | ||
The following configuration options are supported: | ||
|
||
`url (required)`: The address to send logs and traces\ | ||
`apitoken` : API Token of Logicmonitor\ | ||
`headers`: Headers of POST requests\ | ||
`log_batching_enabled`(default = true) : The flag to enable/disable batching of logs\ | ||
`log_batching_interval`(default = 10s) : The time interval for batching of logs | ||
|
||
## Prerequisite: | ||
Below environment variable must be provided | ||
|
||
| Key | Value | | ||
| ------ | ------ | | ||
| LOGICMONITOR_ACCOUNT | Company name | | ||
|
||
## Example | ||
Ingestion through API Token | ||
|
||
```yaml | ||
exporters: | ||
logicmonitor: | ||
url: "https://<company_name>.logicmonitor.com/rest" | ||
apitoken: | ||
access_id: "<access_id of logicmonitor>" | ||
access_key: "<access_key of logicmonitor>" | ||
``` | ||
OR | ||
|
||
Ingestion through Bearer token | ||
|
||
```yaml | ||
exporters: | ||
logicmonitor: | ||
url: "https://<company_name>.logicmonitor.com/rest" | ||
headers: | ||
Authorization: Bearer <bearer token of logicmonitor> | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package logicmonitorexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/logicmonitorexporter" | ||
|
||
import ( | ||
"crypto/hmac" | ||
"crypto/sha256" | ||
"encoding/base64" | ||
"encoding/hex" | ||
"fmt" | ||
"os" | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
// LMAuthenticator is used for authenticating requests to Logicmonitor platform | ||
type LMAuthenticator struct { | ||
Config *Config | ||
} | ||
Comment on lines
+29
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why public? Who is using this? |
||
|
||
// GetCredentials implements AuthProvider interface (https://github.com/logicmonitor/lm-data-sdk-go/blob/b135130a6cb411007cb4f8866966f00c94b7c63a/model/authprovider.go#L3) | ||
func (a LMAuthenticator) GetCredentials(method, uri string, body []byte) string { | ||
// Fetches token from config file, if present | ||
// OR reads token from environment variable | ||
authToken := getToken(a.Config.APIToken, a.Config.Headers) | ||
if authToken.accessID != "" && authToken.accessKey != "" { | ||
return generateLMv1Token(method, authToken.accessID, authToken.accessKey, body, uri).String() | ||
} else if authToken.bearerToken != "" { | ||
return authToken.bearerToken | ||
} | ||
return "" | ||
} | ||
|
||
func getAccessToken(apitoken map[string]string) (string, string, bool) { | ||
accessID, accessKey := "", "" | ||
for k, v := range apitoken { | ||
if strings.EqualFold(k, "access_id") { | ||
accessID = v | ||
} else if strings.EqualFold(k, "access_key") { | ||
accessKey = v | ||
} | ||
} | ||
if accessID != "" && accessKey != "" { | ||
return accessID, accessKey, true | ||
} | ||
return "", "", false | ||
} | ||
|
||
func getBearerToken(headers map[string]string) (string, bool) { | ||
bearerToken := "" | ||
for k, v := range headers { | ||
if strings.EqualFold(k, "authorization") { | ||
bearerToken = strings.Split(v, " ")[1] | ||
return bearerToken, true | ||
} | ||
} | ||
return "", false | ||
} | ||
|
||
type AuthToken struct { | ||
accessID string | ||
accessKey string | ||
bearerToken string | ||
} | ||
|
||
func getToken(apiToken, headers map[string]string) AuthToken { | ||
accessID, accessKey, ok := getAccessToken(apiToken) | ||
if !ok { | ||
accessID = os.Getenv("LOGICMONITOR_ACCESS_ID") | ||
accessKey = os.Getenv("LOGICMONITOR_ACCESS_KEY") | ||
} | ||
bearerToken, ok := getBearerToken(headers) | ||
if !ok { | ||
bearerToken = os.Getenv("LOGICMONITOR_BEARER_TOKEN") | ||
} | ||
authToken := AuthToken{ | ||
accessID: accessID, | ||
accessKey: accessKey, | ||
bearerToken: bearerToken, | ||
} | ||
return authToken | ||
} | ||
|
||
type Lmv1Token struct { | ||
AccessID string | ||
Signature string | ||
Epoch time.Time | ||
} | ||
|
||
func (t *Lmv1Token) String() string { | ||
builder := strings.Builder{} | ||
appendSignature := func(s string) { | ||
if _, err := builder.WriteString(s); err != nil { | ||
fmt.Println(err) //TODO: print err in place of panic | ||
} | ||
} | ||
appendSignature("LMv1 ") | ||
appendSignature(t.AccessID) | ||
appendSignature(":") | ||
appendSignature(t.Signature) | ||
appendSignature(":") | ||
appendSignature(strconv.FormatInt(t.Epoch.UnixNano()/1000000, 10)) | ||
|
||
return builder.String() | ||
} | ||
|
||
// GenerateLMv1Token generates LMv1 Token | ||
func generateLMv1Token(method string, accessID string, accessKey string, body []byte, resourcePath string) *Lmv1Token { | ||
|
||
epochTime := time.Now() | ||
epoch := strconv.FormatInt(epochTime.UnixNano()/1000000, 10) | ||
|
||
methodUpper := strings.ToUpper(method) | ||
|
||
h := hmac.New(sha256.New, []byte(accessKey)) | ||
|
||
writeOrPanic := func(bs []byte) { | ||
if _, err := h.Write(bs); err != nil { | ||
fmt.Println(err) //TODO: print err in place of panic | ||
} | ||
} | ||
writeOrPanic([]byte(methodUpper)) | ||
writeOrPanic([]byte(epoch)) | ||
if body != nil { | ||
writeOrPanic(body) | ||
} | ||
writeOrPanic([]byte(resourcePath)) | ||
|
||
hash := h.Sum(nil) | ||
hexString := hex.EncodeToString(hash) | ||
signature := base64.StdEncoding.EncodeToString([]byte(hexString)) | ||
return &Lmv1Token{ | ||
AccessID: accessID, | ||
Signature: signature, | ||
Epoch: epochTime, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package logicmonitorexporter | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestGetCredentials(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
auth LMAuthenticator | ||
wantErr bool | ||
}{ | ||
{ | ||
"Get Credentials: LMv1 Config", | ||
LMAuthenticator{ | ||
Config: &Config{ | ||
APIToken: map[string]string{"access_id": "testid", "access_key": "testkey"}, | ||
}, | ||
}, | ||
false, | ||
}, | ||
{ | ||
"Get Credentials: Bearer Config", | ||
LMAuthenticator{ | ||
Config: &Config{ | ||
Headers: map[string]string{"Authorization": "Bearer bearervalue"}, | ||
}, | ||
}, | ||
false, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
t.Setenv("LOGICMONITOR_ACCOUNT", "localdev") | ||
cred := tt.auth.GetCredentials("GET", "/to/path", []byte("")) | ||
if cred == "" { | ||
t.Errorf("GetCredentials() no credentials found..") | ||
return | ||
} | ||
}) | ||
} | ||
|
||
} | ||
|
||
func TestGetCredentials_BearerTokenEnv(t *testing.T) { | ||
t.Setenv("LOGICMONITOR_ACCOUNT", "localdev") | ||
t.Setenv("LOGICMONITOR_BEARER_TOKEN", "Bearer newbearertoken") | ||
lmauth := LMAuthenticator{ | ||
Config: &Config{}, | ||
} | ||
cred := lmauth.GetCredentials("GET", "/to/path", []byte("")) | ||
if cred == "" { | ||
t.Errorf("GetCredentials() no credentials found..") | ||
return | ||
} | ||
} | ||
|
||
func TestGetCredentials_APITokenEnv(t *testing.T) { | ||
t.Setenv("LOGICMONITOR_ACCOUNT", "localdev") | ||
t.Setenv("LOGICMONITOR_ACCESS_ID", "newaccessid") | ||
t.Setenv("LOGICMONITOR_ACCESS_KEY", "newaccesskey") | ||
lmauth := LMAuthenticator{ | ||
Config: &Config{}, | ||
} | ||
cred := lmauth.GetCredentials("GET", "/to/path", []byte("")) | ||
if cred == "" { | ||
t.Errorf("GetCredentials() no credentials found..") | ||
return | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same block please