Skip to content

Commit

Permalink
add huaweicloudlogsreceiver skelethon
Browse files Browse the repository at this point in the history
  • Loading branch information
narcis.gemene committed Sep 6, 2024
1 parent 6926554 commit c7fd9e4
Show file tree
Hide file tree
Showing 22 changed files with 2,113 additions and 0 deletions.
1 change: 1 addition & 0 deletions receiver/huaweicloudlogsreceiver/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
205 changes: 205 additions & 0 deletions receiver/huaweicloudlogsreceiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
# Huawei Cloud CES Receiver

<!-- status autogenerated section -->
| Status | |
| ------------- |-----------|
| Stability | [development]: logs |
| Distributions | [] |
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Fhuaweicloudlogs%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Fhuaweicloudlogs) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Fhuaweicloudlogs%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Fhuaweicloudlogs) |
| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@heitorganzeli](https://www.github.com/heitorganzeli), [@narcis96](https://www.github.com/narcis96) |

[development]: https://github.com/open-telemetry/opentelemetry-collector#development
<!-- end autogenerated section -->

This receiver contains the implementation of the Huawei Cloud [Log Tank Service](https://www.huaweicloud.com/intl/en-us/product/lts.html) (LTS) receiver for the OpenTelemetry Collector. The receiver collects logs from Huawei Cloud's LTS service and sends them to the OpenTelemetry Collector for processing and exporting.

## Configuration

The following settings are required:

- `project_id`: The ID of the project in Huawei Cloud. This is used to identify which project's logs are to be collected. See [Obtaining a Project ID](https://support.huaweicloud.com/intl/en-us/devg-apisign/api-sign-provide-proid.html).

- `region_id`: The ID of the Huawei Cloud region from which logs are collected. For example, `eu-west-101`. The full list of the available regions can be found [here](https://pkg.go.dev/github.com/huaweicloud/[email protected]/services/lts/v2/region).

- `log_group_id`: A string indicating the ID of the log group.

- `log_stream_id`: A string indicating the ID of the log stream. See [Obtaining Log Group and Log Stream IDs](https://support.huaweicloud.com/intl/en-us/api-lts/lts_api_0006.html#section1).

- `no_verify_ssl`: A boolean flag indicating whether SSL verification should be disabled. Set to True to disable SSL verification.

- `access_key`: The access key needed for LTS authentification. Check `Huawei Cloud SDK Authentication Setup` section for more details.

- `secret_key`: The secret key needed for LTS authentification. Check `Huawei Cloud SDK Authentication Setup` section for more details.

The following settings are optional:

- `initial_delay`: The delay before the first collection of logs begins. This is a duration field, such as 5s for 5 seconds.

- `collection_interval` (default = `60s`): This is the interval at which this receiver collects logs. This value must be a string readable by Golang's [time.ParseDuration](https://pkg.go.dev/time#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. We recommend a polling interval of at least one minute.

- `retry_on_failure`: The following configurations can be used to control the retry policy of the LTS client. The default values are suitable for most deployment scenarios.
- `enabled` (default true)
- `initial_interval` (default 100ms)
- `max_interval` (default 1s)
- `max_elapsed_time` (default 15s)
- `randomization_factor` (default 0.5)
- `multiplier` (default 1.5)

### Example Configuration

```yaml
receivers:
huaweicloudlogsreceiver:
collection_interval: 3h
initial_delay: 5s
access_key: ${env:HUAWEICLOUD_SDK_AK}
secret_key: ${env:HUAWEICLOUD_SDK_SK}
project_id: project_1
region_id: eu-west-101
log_group_id: test-group-id
log_stream_id: test-stream-id
no_verify_ssl: True
```
The full list of settings exposed for this receiver are documented [here](./config.go).
### Huawei Cloud SDK Authentication Setup
To ensure secure authentication, the Access Key (AK) and Secret Key (SK) used by the Huawei Cloud SDK must be stored in environment variables. See [Obtaining an AK/SK](https://support.huaweicloud.com/intl/en-us/devg-apisign/api-sign-provide-aksk.html).
Before running the application, you need to set the environment variables `HUAWEICLOUD_SDK_AK` and `HUAWEICLOUD_SDK_SK` in your local environment. Here’s how you can do it:

1. Open your terminal.
2. Set the environment variables by executing the following commands:

```sh
export HUAWEICLOUD_SDK_AK=your-access-key
export HUAWEICLOUD_SDK_SK=your-secret-key
```

3. Verify that the variables are set correctly:

```sh
echo $HUAWEICLOUD_SDK_AK
echo $HUAWEICLOUD_SDK_SK
```

## Error handling
If you encounter any LTS errors, please refer to the [Huawei Cloud Error Codes](https://support.huaweicloud.com/intl/en-us/ae-ad-1-api-lts/lts_api_0021.html).

## Converting LTS Log Representation to OpenTelemetry Log Representation

| Source Field | Target Field | Description |
|---------------------------|----------------------------------------------------|---------------------------------------------------------------------------------------------------|
| **Log Content (timestamp)** | `logRecord.observedTimestamp` | The timestamp extracted from the log content. It follows the layout `"2006-01-02/15:04:05"`. Converted to Unix time in nanoseconds. |
| **Log Content (body)** | `logRecord.body` | The main content of the log. Stored as a string in the OTLP log body. |
| **Labels (key)** | `logRecord.attributes` | Each label from the LTS log is converted to an attribute in the OTLP log, where the label's key is used as the attribute's key. |
| **Labels (value)** | `logRecord.attributes` | The corresponding value of each LTS log label key is stored as the attribute's value. |
| **Line Number** | `logRecord.attributes.lineNum` | The line number from the LTS log, if available. Stored as an attribute with the key `lineNum`. |
| **Project ID** | `resource.attributes.project.id` | The project ID used in the configuration file of the receiver.. |
| **Region ID** | `resource.attributes.region.id` | The region id used in the configuration file of the receiver. |
| **Group ID** | `resource.attributes.group.id` | The log group id used in the configuration file of the receiver. |
| **Stream ID** | `resource.attributes.stream.id` | The log stream id used in the configuration file of the receiver. |
| *N/A* | `resource.attributes.cloud.provider` | Set to `"huawei_cloud"` as the cloud provider. |
| *N/A* | `scopeLogs.scope.name` | Set to `"huawei_cloud_lts"` as the scope name. |
| *N/A* | `scopeLogs.scope.version` | Set to `"v2"` as the scope version. |

This mapping ensures that logs from Huawei Cloud's LTS can be seamlessly integrated into systems using the OpenTelemetry Protocol for observability.


### Notes

- The `timestamp` field in the source is converted from milliseconds to nanoseconds in the target field.
- Some fields are added in the target format with constant values to provide additional context and metadata.

### Example:

```json
[
{
"content": "2020-07-25/14:40:00 this logis Error NO 2",
"line_num": "123",
"labels": {
"hostIP": "192.168.0.156",
"hostId": "9787ef31-f171-4eff-ba71-72d580f11f60",
"podName": "default_procname",
"clusterId": "CONFIG_FILE",
"nameSpace":"CONFIG_FILE",
"category": "LTS",
}
},
{
"content": "2020-07-25/14:50:00 this logis Error NO 3",
"line_num": "456",
"labels": {
"hostIP": "192.168.0.156",
"hostId": "9787ef31-f171-4eff-ba71-72d580f11f60",
"podName": "default_procname",
"clusterId": "CONFIG_FILE",
"nameSpace":"CONFIG_FILE",
"category": "LTS",
}
},
]
```

converts to

```json
{
"resourceLogs": [
{
"resource": {
"attributes": {
"cloud.provider": "huawei_cloud",
"project.id": "project1",
"region.id": "region1",
"group.id": "group1",
"stream.id":"stream1"
},
"scopeLogs": [
{
"scope": {
"name": "huawei_cloud_lts",
"version": "v2"
},
"logRecords": [
{
"observedTimeUnixNano": "1595688000000000000",
"body": {
"stringValue": "2020-07-25/14:40:00 this log is Error NO 2"
},
"attributes": {
"hostIP": "192.168.0.156",
"hostId": "9787ef31-f171-4eff-ba71-72d580f11f60",
"podName": "default_procname",
"clusterId": "CONFIG_FILE",
"nameSpace":"CONFIG_FILE",
"category": "LTS",
"lineNum": "123"
},
},
{
"observedTimeUnixNano": "1595688600000000000",
"body": {
"stringValue": "2020-07-25/14:50:00 this log is Error NO 3"
},
"attributes": {
"hostIP": "192.168.0.156",
"hostId": "9787ef31-f171-4eff-ba71-72d580f11f60",
"podName": "default_procname",
"clusterId": "CONFIG_FILE",
"nameSpace":"CONFIG_FILE",
"category": "LTS",
"lineNum": "456"
},
}
]
}
]
}
}
]
}
```
122 changes: 122 additions & 0 deletions receiver/huaweicloudlogsreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package huaweicloudlogsreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver"

import (
"errors"
"net/url"
"strconv"

"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/config/configopaque"
"go.opentelemetry.io/collector/config/configretry"
"go.opentelemetry.io/collector/receiver/scraperhelper"
"go.uber.org/multierr"
)

var (
// Predefined error responses for configuration validation failures
errMissingProjectID = errors.New(`"project_id" is not specified in config`)
errMissingRegionID = errors.New(`"region_id" is not specified in config`)
errMissingGroupID = errors.New(`"log_group_id" is not specified in config`)
errMissingStreamID = errors.New(`"log_stream_id" is not specified in config`)

errInvalidProxy = errors.New(`"proxy_address" must be specified if "proxy_user" or "proxy_password" is set"`)
)

// Config represent a configuration for the CloudWatch logs exporter.
type Config struct {
scraperhelper.ControllerConfig `mapstructure:",squash"`
confighttp.ClientConfig `mapstructure:",squash"`
// Set of attributes used to configure huawei's CES SDK connection
HuaweiSessionConfig `mapstructure:",squash"`

// ProjectID is a string to reference project where logs should be associated with.
// If ProjectID is not filled in, the SDK will automatically call the IAM service to query the project id corresponding to the region.
ProjectID string `mapstructure:"project_id"`

// RegionID is the ID of the LTS region.
RegionID string `mapstructure:"region_id"`

// GroupID is the ID of the LTS log group.
GroupID string `mapstructure:"log_group_id"`

// Stream is the ID of the LTS log stream.
StreamID string `mapstructure:"log_stream_id"`

BackOffConfig configretry.BackOffConfig `mapstructure:"retry_on_failure"`
}

type HuaweiSessionConfig struct {
AccessKey configopaque.String `mapstructure:"access_key"`

SecretKey configopaque.String `mapstructure:"secret_key"`
// Number of seconds before timing out a request.
NoVerifySSL bool `mapstructure:"no_verify_ssl"`
// Upload segments to AWS X-Ray through a proxy.
ProxyAddress string `mapstructure:"proxy_address"`
ProxyUser string `mapstructure:"proxy_user"`
ProxyPassword string `mapstructure:"proxy_password"`
}

var _ component.Config = (*Config)(nil)

// Validate config
func (config *Config) Validate() error {
var err error
if config.ProjectID == "" {
err = multierr.Append(err, errMissingProjectID)
}
if config.RegionID == "" {
err = multierr.Append(err, errMissingRegionID)
}
if config.GroupID == "" {
err = multierr.Append(err, errMissingGroupID)
}
if config.StreamID == "" {
err = multierr.Append(err, errMissingStreamID)
}

// Validate that ProxyAddress is provided if ProxyUser or ProxyPassword is set
if (config.ProxyUser != "" || config.ProxyPassword != "") && config.ProxyAddress == "" {
err = multierr.Append(err, errInvalidProxy)
}

return err
}

func createHTTPConfig(cfg HuaweiSessionConfig) (*config.HttpConfig, error) {
if cfg.ProxyAddress == "" {
return config.DefaultHttpConfig().WithIgnoreSSLVerification(cfg.NoVerifySSL), nil
}
proxy, err := configureHTTPProxy(cfg)
if err != nil {
return nil, err
}
return config.DefaultHttpConfig().WithProxy(proxy), nil
}

func configureHTTPProxy(cfg HuaweiSessionConfig) (*config.Proxy, error) {
proxyURL, err := url.Parse(cfg.ProxyAddress)
if err != nil {
return nil, err
}

proxy := config.NewProxy().
WithSchema(proxyURL.Scheme).
WithHost(proxyURL.Hostname())
if len(proxyURL.Port()) > 0 {
if i, err := strconv.Atoi(proxyURL.Port()); err == nil {
proxy = proxy.WithPort(i)
}
}

// Configure the username and password if the proxy requires authentication
if len(cfg.ProxyUser) > 0 {
proxy = proxy.WithUsername(cfg.ProxyUser).WithPassword(cfg.ProxyPassword)
}
return proxy, nil
}
Loading

0 comments on commit c7fd9e4

Please sign in to comment.