Skip to content

Commit

Permalink
Add Logicmonitor exporter overall structure (open-telemetry#16386)
Browse files Browse the repository at this point in the history
* Add Logicmonitor Exporter overall structure

* Initiate default retry and queue settings

* Add Status details in README.md

* Remove batching and replace full config
Removed batching logic from logs exporter.
Replaced full test config.yaml with component specific details.

* Update dependencies to v0.66.0 and review comment change in config.go

* Fix failed check of ISSUE TEMPLATE

* Upgrade collector version and fix deprecated functions
  • Loading branch information
khyatigandhi6 committed Nov 29, 2022
1 parent 9505f77 commit aed6330
Show file tree
Hide file tree
Showing 15 changed files with 1,053 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .chloggen/logicmonitorexporter-overall-skeleton.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
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
47 changes: 47 additions & 0 deletions exporter/logicmonitorexporter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# LogicMonitor Exporter

| Status | |
| ------ | ------ |
| Stablilty | traces [development](https://github.com/open-telemetry/opentelemetry-collector#development) |
| | logs [development](https://github.com/open-telemetry/opentelemetry-collector#development) |
| Supported pipeline types | traces,logs |
| Distributions | [contrib](https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib) |


This exporter supports sending logs and traces data to [Logicmonitor](https://www.logicmonitor.com/).

## Configuration Options
The following configuration options are supported:

`endpoint (required)`: The address to send logs and traces\
`apitoken` : API Token of Logicmonitor

## Prerequisite
Below environment variable must be provided

| Key | Value |
| ------ | ------ |
| LOGICMONITOR_ACCOUNT | Company name |

## Example
Ingestion through API Token

```yaml
exporters:
logicmonitor:
endpoint: "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:
endpoint: "https://<company_name>.logicmonitor.com/rest"
headers:
Authorization: Bearer <bearer token of logicmonitor>
```
57 changes: 57 additions & 0 deletions exporter/logicmonitorexporter/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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:https://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 (
"fmt"
"net/url"

"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/exporter/exporterhelper"

"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry"
)

// Config defines configuration for LogicMonitor exporter.
type Config struct {
config.ExporterSettings `mapstructure:",squash"`

confighttp.HTTPClientSettings `mapstructure:",squash"`

exporterhelper.QueueSettings `mapstructure:"sending_queue"`
exporterhelper.RetrySettings `mapstructure:"retry_on_failure"`
ResourceToTelemetrySettings resourcetotelemetry.Settings `mapstructure:"resource_to_telemetry_conversion"`

// ApiToken of Logicmonitor Platform
APIToken APIToken `mapstructure:"apitoken"`
}

type APIToken struct {
AccessID string `mapstructure:"access_id"`
AccessKey string `mapstructure:"access_key"`
}

func (c *Config) Validate() error {
if c.Endpoint == "" {
return fmt.Errorf("Endpoint should not be empty")
}

u, err := url.Parse(c.Endpoint)
if err != nil || u.Scheme == "" || u.Host == "" {
return fmt.Errorf("Endpoint must be valid")
}
return nil
}
153 changes: 153 additions & 0 deletions exporter/logicmonitorexporter/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// 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:https://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 (
"errors"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/confmap/confmaptest"
"go.opentelemetry.io/collector/exporter/exporterhelper"
)

func TestConfigValidation(t *testing.T) {
testcases := []struct {
name string
cfg *Config
wantErr bool
errorMessage string
}{
{
name: "empty endpoint",
cfg: &Config{
HTTPClientSettings: confighttp.HTTPClientSettings{
Endpoint: "",
},
},
wantErr: true,
errorMessage: "Endpoint should not be empty",
},
{
name: "missing http scheme",
cfg: &Config{
HTTPClientSettings: confighttp.HTTPClientSettings{
Endpoint: "test.com/dummy",
},
},
wantErr: true,
errorMessage: "Endpoint must be valid",
},
{
name: "invalid endpoint format",
cfg: &Config{
HTTPClientSettings: confighttp.HTTPClientSettings{
Endpoint: "invalid.com@#$%",
},
},
wantErr: true,
errorMessage: "Endpoint must be valid",
},
{
name: "valid config",
cfg: &Config{
HTTPClientSettings: confighttp.HTTPClientSettings{
Endpoint: "http:https://validurl.com/rest",
},
},
wantErr: false,
errorMessage: "",
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
err := tc.cfg.Validate()
if (err != nil) != tc.wantErr {
t.Errorf("config validation failed: error = %v, wantErr %v", err, tc.wantErr)
return
}
if tc.wantErr {
assert.Error(t, err)
if len(tc.errorMessage) != 0 {
assert.Equal(t, errors.New(tc.errorMessage), err, "Error messages must match")
}
return
}
assert.NoError(t, err)
})
}
}

func TestLoadConfig(t *testing.T) {
t.Parallel()

cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
require.NoError(t, err)

tests := []struct {
id component.ID
expected component.Config
}{
{
id: component.NewIDWithName(typeStr, "apitoken"),
expected: &Config{
ExporterSettings: config.NewExporterSettings(component.NewID(typeStr)),
RetrySettings: exporterhelper.NewDefaultRetrySettings(),
QueueSettings: exporterhelper.NewDefaultQueueSettings(),
HTTPClientSettings: confighttp.HTTPClientSettings{
Endpoint: "https://company.logicmonitor.com/rest",
},
APIToken: APIToken{
AccessID: "accessid",
AccessKey: "accesskey",
},
},
},
{
id: component.NewIDWithName(typeStr, "bearertoken"),
expected: &Config{
ExporterSettings: config.NewExporterSettings(component.NewID(typeStr)),
RetrySettings: exporterhelper.NewDefaultRetrySettings(),
QueueSettings: exporterhelper.NewDefaultQueueSettings(),
HTTPClientSettings: confighttp.HTTPClientSettings{
Endpoint: "https://company.logicmonitor.com/rest",
Headers: map[string]string{
"Authorization": "Bearer <token>",
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.id.String(), func(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()

sub, err := cm.Sub(tt.id.String())
require.NoError(t, err)
require.NoError(t, component.UnmarshalConfig(sub, cfg))

assert.NoError(t, component.ValidateConfig(cfg))
assert.Equal(t, tt.expected, cfg)
})
}
}
Loading

0 comments on commit aed6330

Please sign in to comment.