Skip to content
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

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .chloggen/logicmonitorexporter-implementation.yaml
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:
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ exporter/jaegerexporter/ @open-telemetry/collector-c
exporter/jaegerthrifthttpexporter/ @open-telemetry/collector-contrib-approvers @jpkrohling @pavolloffay
exporter/kafkaexporter/ @open-telemetry/collector-contrib-approvers @pavolloffay @MovieStoreGuy
exporter/loadbalancingexporter/ @open-telemetry/collector-contrib-approvers @jpkrohling
exporter/logicmonitorexporter/ @open-telemetry/collector-contrib-approvers @bogdandrutu @khyatigandhi6 @avadhut123pisal
exporter/logzioexporter/ @open-telemetry/collector-contrib-approvers @jkowall @Doron-Bargo @yotamloe
exporter/lokiexporter/ @open-telemetry/collector-contrib-approvers @gramidt @gouthamve @jpkrohling @kovrus @mar4uk
exporter/mezmoexporter/ @open-telemetry/collector-contrib-approvers @dashpole @billmeyer @gjanco @jsumners
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ body:
- exporter/jaegerthrifthttp
- exporter/kafka
- exporter/loadbalancing
- exporter/logicmonitor
- exporter/logzio
- exporter/loki
- exporter/mezmo
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ body:
- exporter/jaegerthrifthttp
- exporter/kafka
- exporter/loadbalancing
- exporter/logicmonitor
- exporter/logzio
- exporter/loki
- exporter/mezmo
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/other.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ body:
- exporter/jaegerthrifthttp
- exporter/kafka
- exporter/loadbalancing
- exporter/logicmonitor
- exporter/logzio
- exporter/loki
- exporter/mezmo
Expand Down
5 changes: 5 additions & 0 deletions cmd/configschema/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ require (
golang.org/x/text v0.4.0
)

require github.com/logicmonitor/lm-data-sdk-go v0.5.0 // indirect

require (
Comment on lines +17 to 19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same block please

bitbucket.org/atlassian/go-asap/v2 v2.6.0 // indirect
cloud.google.com/go v0.105.0 // indirect
Expand Down Expand Up @@ -337,6 +339,7 @@ require (
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/jaegerthrifthttpexporter v0.64.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.64.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter v0.64.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/logicmonitorexporter v0.64.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/logzioexporter v0.64.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/lokiexporter v0.64.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/mezmoexporter v0.64.0 // indirect
Expand Down Expand Up @@ -738,6 +741,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafka

replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter => ../../exporter/loadbalancingexporter

replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/logicmonitorexporter => ../../exporter/logicmonitorexporter

replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/logzioexporter => ../../exporter/logzioexporter

replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/lokiexporter => ../../exporter/lokiexporter
Expand Down
2 changes: 2 additions & 0 deletions cmd/configschema/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions exporter/logicmonitorexporter/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line.

41 changes: 41 additions & 0 deletions exporter/logicmonitorexporter/README.md
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>
```
150 changes: 150 additions & 0 deletions exporter/logicmonitorexporter/authenticator.go
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
Copy link
Member

Choose a reason for hiding this comment

The 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,
}
}
85 changes: 85 additions & 0 deletions exporter/logicmonitorexporter/authenticator_test.go
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
}
}
Loading