Skip to content

Commit f658cf2

Browse files
authored
[exporter/azureblob] Fix Azure Blob Storage name to avoid breaking file extension (#39606)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Add Fix Azure Blob Storage name to avoid breaking file extension. Previously the final blob names looked like the following: ``` 2025/04/24/traces_00_59_20.json_6651 ``` ^(this is the actual result I took from one of the raw logs generated from running the unit test) Now they properly append the random number before the file extension: ``` 2025/04/24/traces_00_59_49_4913.json ``` EDIT: This logic has now been made configurable instead of the default. <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes #39593 <!--Describe what testing was performed and which tests were added.--> #### Testing Added a new test `TestGenerateBlobNameSerialNumBefore` <!--Describe the documentation added.--> #### Documentation Updated the `README.md` with the new config option <!--Please delete paragraphs that you did not use before submitting.-->
1 parent 4a5c06c commit f658cf2

File tree

5 files changed

+85
-6
lines changed

5 files changed

+85
-6
lines changed

.chloggen/fix_azure_blob_name.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: 'enhancement'
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: azureblobexporter
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "Add SerialNumBeforeExtension option to BlobNameFormat in Azure Blob exporter as an option to avoid breaking file extension"
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [39593]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

exporter/azureblobexporter/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ The following settings can be optionally configured and have default values:
3636
- logs_format (default `2006/01/02/logs_15_04_05.json`): blob name format.
3737
- traces_format (default `2006/01/02/traces_15_04_05.json`): blob name format.
3838
- serial_num_range (default `10000`): a range of random number to be appended after blob_name. e.g. `blob_name_{serial_num}`.
39+
- serial_num_before_extension (default `false`): places the serial number before the file extension if there is one. e.g `blob_name_{serial_num}.json` instead of `blob_name.json_{serial_num}`
3940
- format (default `json`): `json` or `proto`. which present otel json or otel protobuf format, the file extension will be `json` or `pb`.
4041
- encodings (default using encoding specified in `format`, which is `json`): if specified, uses the encoding extension to encode telemetry data. Overrides format.
4142
- logs (default `nil`): encoding component id.

exporter/azureblobexporter/config.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ type (
2727
)
2828

2929
type BlobNameFormat struct {
30-
MetricsFormat string `mapstructure:"metrics_format"`
31-
LogsFormat string `mapstructure:"logs_format"`
32-
TracesFormat string `mapstructure:"traces_format"`
33-
SerialNumRange int64 `mapstructure:"serial_num_range"`
34-
Params map[string]string `mapstructure:"params"`
30+
MetricsFormat string `mapstructure:"metrics_format"`
31+
LogsFormat string `mapstructure:"logs_format"`
32+
TracesFormat string `mapstructure:"traces_format"`
33+
SerialNumRange int64 `mapstructure:"serial_num_range"`
34+
SerialNumBeforeExtension bool `mapstructure:"serial_num_before_extension"`
35+
Params map[string]string `mapstructure:"params"`
3536
}
3637

3738
type AppendBlob struct {

exporter/azureblobexporter/exporter.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"fmt"
1111
"io"
1212
"math/rand/v2"
13+
"path/filepath"
14+
"strings"
1315
"time"
1416

1517
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
@@ -162,7 +164,18 @@ func (e *azureBlobExporter) generateBlobName(signal pipeline.Signal) (string, er
162164
default:
163165
return "", fmt.Errorf("unsupported signal type: %v", signal)
164166
}
165-
return fmt.Sprintf("%s_%d", now.Format(format), randomInRange(0, int(e.config.BlobNameFormat.SerialNumRange))), nil
167+
var blobName string
168+
if e.config.BlobNameFormat.SerialNumBeforeExtension {
169+
// Append a random number and do so before the file extension if there is one
170+
ext := filepath.Ext(format)
171+
formatWithoutExt := strings.TrimSuffix(format, ext)
172+
randInt := randomInRange(0, int(e.config.BlobNameFormat.SerialNumRange))
173+
blobName = fmt.Sprintf("%s_%d%s", now.Format(formatWithoutExt), randInt, ext)
174+
} else {
175+
// Appends the random number after any potential file extension to minimize performance impact when high throughput
176+
blobName = fmt.Sprintf("%s_%d", now.Format(format), randomInRange(0, int(e.config.BlobNameFormat.SerialNumRange)))
177+
}
178+
return blobName, nil
166179
}
167180

168181
func (e *azureBlobExporter) Capabilities() consumer.Capabilities {

exporter/azureblobexporter/exporter_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,43 @@ func TestGenerateBlobName(t *testing.T) {
163163
assert.True(t, strings.HasPrefix(tracesBlobName, now.Format(c.BlobNameFormat.TracesFormat)))
164164
}
165165

166+
func TestGenerateBlobNameSerialNumBefore(t *testing.T) {
167+
t.Parallel()
168+
169+
c := &Config{
170+
BlobNameFormat: &BlobNameFormat{
171+
MetricsFormat: "2006/01/02/metrics_15_04_05.json",
172+
LogsFormat: "2006/01/02/logs_15_04_05.json",
173+
TracesFormat: "2006/01/02/traces_15_04_05", // no extension
174+
SerialNumRange: 10000,
175+
SerialNumBeforeExtension: true,
176+
Params: map[string]string{},
177+
},
178+
}
179+
180+
ae := newAzureBlobExporter(c, zaptest.NewLogger(t), pipeline.SignalMetrics)
181+
182+
assertFormat := func(blobName string, format string) {
183+
ext := filepath.Ext(format)
184+
formatWithoutExt := strings.TrimSuffix(format, ext)
185+
assert.True(t, strings.HasPrefix(blobName, formatWithoutExt))
186+
assert.True(t, strings.HasSuffix(blobName, ext))
187+
}
188+
189+
now := time.Now()
190+
metricsBlobName, err := ae.generateBlobName(pipeline.SignalMetrics)
191+
assert.NoError(t, err)
192+
assertFormat(metricsBlobName, now.Format(c.BlobNameFormat.MetricsFormat))
193+
194+
logsBlobName, err := ae.generateBlobName(pipeline.SignalLogs)
195+
assert.NoError(t, err)
196+
assertFormat(logsBlobName, now.Format(c.BlobNameFormat.LogsFormat))
197+
198+
tracesBlobName, err := ae.generateBlobName(pipeline.SignalTraces)
199+
assert.NoError(t, err)
200+
assertFormat(tracesBlobName, now.Format(c.BlobNameFormat.TracesFormat))
201+
}
202+
166203
func getMockAzBlobClient() *mockAzBlobClient {
167204
mockAzBlobClient := &mockAzBlobClient{
168205
url: "https://fakeaccount.blob.core.windows.net/",

0 commit comments

Comments
 (0)