Skip to content

Commit aa315a5

Browse files
LikeTheSaladotelbot[bot]breedx-splk
authored
Disk buffering api implementation (#2183)
Co-authored-by: otelbot <[email protected]> Co-authored-by: jason plumb <[email protected]>
1 parent 89b99af commit aa315a5

File tree

63 files changed

+1357
-2265
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1357
-2265
lines changed

disk-buffering/DESIGN.md

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,62 @@
11
# Design Overview
22

3-
There are three main disk-writing exporters provided by this module:
3+
The core of disk buffering
4+
is [SignalStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/SignalStorage.java).
5+
SignalStorage is an abstraction that defines the bare minimum functionalities needed for
6+
implementations to allow writing and reading signals.
47

5-
* [LogRecordToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/LogRecordToDiskExporter.java)
6-
* [MetricToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/MetricToDiskExporter.java)
7-
* [SpanToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/SpanToDiskExporter.java))
8+
There is a default implementation per signal that writes serialized signal items to protobuf
9+
delimited messages into files, where each file's name represents a timestamp of when it was created,
10+
which will help later to know when it's ready to read, as well as when it's expired. These
11+
implementations are the following:
812

9-
Each is responsible for writing a specific type of telemetry to disk storage for later
10-
harvest/ingest.
13+
* [FileSpanStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/impl/FileSpanStorage.java)
14+
* [FileLogRecordStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/impl/FileLogRecordStorage.java)
15+
* [FileMetricStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/impl/FileMetricStorage.java)
1116

12-
For later reading, there are:
17+
Each one has a `create()` method that takes a destination directory (to store data into) and an
18+
optional [FileStorageConfiguration](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/impl/FileStorageConfiguration.java)
19+
to have a finer control of the storing behavior.
1320

14-
* [LogRecordFromToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/LogRecordFromDiskExporter.java)
15-
* [MetricFromDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/MetricFromDiskExporter.java)
16-
* [SpanFromDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/SpanFromDiskExporter.java))
21+
Even
22+
though [SignalStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/SignalStorage.java)
23+
can receive signal items directly to be stored in disk, there are convenience exporter
24+
implementations for each signal that handle the storing process on your behalf. Those are the
25+
following:
1726

18-
Each one of those has a `create()` method that takes a delegate exporter (to send data
19-
to ingest) and the `StorageConfiguration` that tells them where to find buffered data.
27+
* [SpanToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/exporters/SpanToDiskExporter.java)
28+
* [LogRecordToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/exporters/LogRecordToDiskExporter.java)
29+
* [MetricToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/exporters/MetricToDiskExporter.java)
2030

21-
As explained in the [README](README.md), this has to be triggered manually by the consumer of
22-
this library and does not happen automatically.
31+
Each receive their
32+
respective [SignalStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/SignalStorage.java)
33+
object to delegate signals to as well as an optional callback object to notify its operations.
2334

2435
## Writing overview
2536

2637
![Writing flow](assets/writing-flow.png)
2738

28-
* The writing process happens automatically within its `export(Collection<SignalData> signals)`
29-
method, which is called by the configured signal processor.
30-
* When a set of signals is received, these are delegated over to
31-
a type-specific wrapper of [ToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/exporter/ToDiskExporter.java)
32-
class which then serializes them using an implementation
33-
of [SignalSerializer](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/serialization/serializers/SignalSerializer.java)
34-
and then the serialized data is appended into a File using an instance of
35-
the [Storage](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/storage/Storage.java)
36-
class.
39+
* Via the convenience toDisk exporters, the writing process happens automatically within their
40+
`export(Collection<SignalData> signals)` method, which is called by the configured signal
41+
processor.
42+
* When a set of signals is received, these are delegated over to a type-specific serializer
43+
and then the serialized data is appended into a file.
3744
* The data is written into a file directly, without the use of a buffer, to make sure no data gets
3845
lost in case the application ends unexpectedly.
39-
* Each disk exporter stores its signals in its own folder, which is expected to contain files
46+
* Each signal storage stores its signals in its own folder, which is expected to contain files
4047
that belong to that type of signal only.
4148
* Each file may contain more than a batch of signals if the configuration parameters allow enough
4249
limit size for it.
4350
* If the configured folder size for the signals has been reached and a new file is needed to be
4451
created to keep storing new data, the oldest available file will be removed to make space for the
4552
new one.
46-
* The [Storage](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/storage/Storage.java),
47-
[FolderManager](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/storage/FolderManager.java)
48-
and [WritableFile](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/storage/files/WritableFile.java)
49-
files contain more information on the details of the writing process into a file.
5053

5154
## Reading overview
5255

5356
![Reading flow](assets/reading-flow.png)
5457

55-
* The reading process has to be triggered manually by the library consumer as explained in
56-
the [README](README.md).
58+
* The reading process has to be triggered manually by the library consumer via the signal storage
59+
iterator.
5760
* A single file is read at a time and updated to remove the data gathered from it after it is
5861
successfully exported, until it's emptied. Each file previously created during the
5962
writing process has a timestamp in milliseconds, which is used to determine what file to start
@@ -62,9 +65,3 @@ this library and does not happen automatically.
6265
the time of creating the disk exporter, then it will be ignored, and the next oldest (and
6366
unexpired) one will be used instead.
6467
* All the stale and empty files will be removed as a new file is created.
65-
* The [Storage](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/storage/Storage.java),
66-
[FolderManager](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/storage/FolderManager.java)
67-
and [ReadableFile](src/main/java/io/opentelemetry/contrib/disk/buffering/internal/storage/files/ReadableFile.java)
68-
files contain more information on the details of the file reading process.
69-
* Note that the reader delegates the data to the exporter exactly in the way it has received the
70-
data - it does not try to batch data (but this could be an optimization in the future).

disk-buffering/README.md

Lines changed: 98 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,132 @@
11
# Disk buffering
22

3-
This module provides exporters that store telemetry data in files which can be
4-
sent later on demand. A high level description of how it works is that there are two separate
5-
processes in place, one for writing data in disk, and one for reading/exporting the previously
6-
stored data.
3+
This module provides an abstraction
4+
named [SignalStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/SignalStorage.java),
5+
as well as default implementations for each signal type that allow writing signals to disk and
6+
reading them later.
77

8-
* Each exporter stores the received data automatically in disk right after it's received from its
9-
processor.
10-
* The reading of the data back from disk and exporting process has to be done manually. At
11-
the moment there's no automatic mechanism to do so. There's more information on how it can be
12-
achieved, under [Reading data](#reading-data).
8+
For a more detailed information on how the whole process works, take a look at
9+
the [DESIGN.md](DESIGN.md) file.
1310

14-
> For a more detailed information on how the whole process works, take a look at
15-
> the [DESIGN.md](DESIGN.md) file.
11+
## Default implementation usage
1612

17-
## Configuration
13+
The default implementations are the following:
1814

19-
The configurable parameters are provided **per exporter**, the available ones are:
15+
* [FileSpanStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/impl/FileSpanStorage.java)
16+
* [FileLogRecordStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/impl/FileLogRecordStorage.java)
17+
* [FileMetricStorage](src/main/java/io/opentelemetry/contrib/disk/buffering/storage/impl/FileMetricStorage.java)
2018

21-
* Max file size, defaults to 1MB.
22-
* Max folder size, defaults to 10MB. All files are stored in a single folder per-signal, therefore
23-
if all 3 types of signals are stored, the total amount of space from disk to be taken by default
24-
would be of 30MB.
25-
* Max age for file writing, defaults to 30 seconds.
26-
* Min age for file reading, defaults to 33 seconds. It must be greater that the max age for file
27-
writing.
28-
* Max age for file reading, defaults to 18 hours. After that time passes, the file will be
29-
considered stale and will be removed when new files are created. No more data will be read from a
30-
file past this time.
31-
32-
## Usage
19+
### Set up
3320

34-
### Storing data
21+
We need to create a signal storage object per signal type to start writing signals to disk. Each
22+
`File*Storage` implementation has a `create()` function that receives:
23+
24+
* A File directory to store the signal files. Note that each signal storage object must have a
25+
dedicated directory to work properly.
26+
* (Optional) a configuration object.
3527

36-
In order to use it, you need to wrap your own exporter with a new instance of
37-
the ones provided in here:
28+
The available configuration parameters are the following:
3829

39-
* For a LogRecordExporter, it must be wrapped within
40-
a [LogRecordToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/LogRecordToDiskExporter.java).
41-
* For a MetricExporter, it must be wrapped within
42-
a [MetricToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/MetricToDiskExporter.java).
43-
* For a SpanExporter, it must be wrapped within
44-
a [SpanToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/SpanToDiskExporter.java).
30+
* Max file size, defaults to 1MB.
31+
* Max folder size, defaults to 10MB.
32+
* Max age for file writing. It sets the time window where a file can get signals appended to it.
33+
Defaults to 30 seconds.
34+
* Min age for file reading. It sets the time to wait before starting to read from a file after
35+
its creation. Defaults to 33 seconds. It must be greater that the max age for file writing.
36+
* Max age for file reading. After that time passes, the file will be considered stale and will be
37+
removed when new files are created. No more data will be read from a file past this time. Defaults
38+
to 18 hours.
4539

46-
Each wrapper will need the following when instantiating them:
40+
```java
41+
// Root dir
42+
File rootDir = new File("/some/root");
4743

48-
* The exporter to be wrapped.
49-
* A File instance of the root directory where all the data is going to be written. The same root dir
50-
can be used for all the wrappers, since each will create their own folder inside it.
51-
* An instance
52-
of [StorageConfiguration](src/main/java/io/opentelemetry/contrib/disk/buffering/config/StorageConfiguration.java)
53-
with the desired parameters. You can create one with default values by
54-
calling `StorageConfiguration.getDefault()`.
44+
// Setting up span storage
45+
SignalStorage.Span spanStorage = FileSpanStorage.create(new File(rootDir, "spans"));
5546

56-
After wrapping your exporters, you must register the wrapper as the exporter you'll use. It will
57-
take care of always storing the data it receives.
47+
// Setting up metric storage
48+
SignalStorage.Metric metricStorage = FileMetricStorage.create(new File(rootDir, "metrics"));
5849

59-
#### Set up example for spans
50+
// Setting up log storage
51+
SignalStorage.LogRecord logStorage = FileLogRecordStorage.create(new File(rootDir, "logs"));
52+
```
6053

61-
### Writing data
54+
### Storing data
6255

63-
The data is written in the disk by "ToDisk" exporters, these are exporters that serialize and store the data as received by their processors. If for some reason
64-
the "ToDisk" cannot store data in the disk, they'll delegate the data to their wrapped exporter.
56+
While you could manually call your `SignalStorage.write(items)` function, disk buffering
57+
provides convenience exporters that you can use in your OpenTelemetry's instance, so
58+
that all signals are automatically stored as they are created.
6559

66-
```java
67-
// Creating the SpanExporter of our choice.
68-
SpanExporter mySpanExporter = OtlpGrpcSpanExporter.getDefault();
60+
* For a span storage, use
61+
a [SpanToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/exporters/SpanToDiskExporter.java).
62+
* For a log storage, use
63+
a [LogRecordToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/exporters/LogRecordToDiskExporter.java).
64+
* For a metric storage, use
65+
a [MetricToDiskExporter](src/main/java/io/opentelemetry/contrib/disk/buffering/exporters/MetricToDiskExporter.java).
6966

70-
// Wrapping our exporter with its "ToDisk" exporter.
71-
SpanToDiskExporter toDiskExporter = SpanToDiskExporter.create(mySpanExporter, StorageConfiguration.getDefault(new File("/my/signals/cache/dir")));
67+
Each will wrap a signal storage for its respective signal type, as well as an optional callback
68+
to notify when it succeeds, fails, and gets shutdown.
7269

73-
// Registering the disk exporter within our OpenTelemetry instance.
74-
SdkTracerProvider myTraceProvider = SdkTracerProvider.builder()
75-
.addSpanProcessor(SimpleSpanProcessor.create(toDiskExporter))
70+
```java
71+
// Setting up span to disk exporter
72+
SpanToDiskExporter spanToDiskExporter =
73+
SpanToDiskExporter.builder(spanStorage).setExporterCallback(spanCallback).build();
74+
// Setting up metric to disk
75+
MetricToDiskExporter metricToDiskExporter =
76+
MetricToDiskExporter.builder(metricStorage).setExporterCallback(metricCallback).build();
77+
// Setting up log to disk exporter
78+
LogRecordToDiskExporter logToDiskExporter =
79+
LogRecordToDiskExporter.builder(logStorage).setExporterCallback(logCallback).build();
80+
81+
// Using exporters in your OpenTelemetry instance.
82+
OpenTelemetry openTelemetry =
83+
OpenTelemetrySdk.builder()
84+
// Using span to disk exporter
85+
.setTracerProvider(
86+
SdkTracerProvider.builder()
87+
.addSpanProcessor(BatchSpanProcessor.builder(spanToDiskExporter).build())
88+
.build())
89+
// Using log to disk exporter
90+
.setLoggerProvider(
91+
SdkLoggerProvider.builder()
92+
.addLogRecordProcessor(
93+
BatchLogRecordProcessor.builder(logToDiskExporter).build())
94+
.build())
95+
// Using metric to disk exporter
96+
.setMeterProvider(
97+
SdkMeterProvider.builder()
98+
.registerMetricReader(PeriodicMetricReader.create(metricToDiskExporter))
99+
.build())
76100
.build();
77-
OpenTelemetrySdk.builder()
78-
.setTracerProvider(myTraceProvider)
79-
.buildAndRegisterGlobal();
80-
81101
```
82102

103+
Now when creating signals using your `OpenTelemetry` instance, those will get stored in disk.
104+
83105
### Reading data
84106

85-
In order to read data, we need to create "FromDisk" exporters, which read data from the disk, parse it and delegate it
86-
to their wrapped exporters.
107+
In order to read data, we can iterate through our signal storage objects and then forward them to
108+
a network exporter, as shown in the example for spans below.
87109

88110
```java
89-
try {
90-
SpanFromDiskExporter fromDiskExporter = SpanFromDiskExporter.create(memorySpanExporter, storageConfig);
91-
if(fromDiskExporter.exportStoredBatch(1, TimeUnit.SECONDS)) {
92-
// A batch was successfully exported and removed from disk. You can call this method for as long as it keeps returning true.
93-
} else {
94-
// Either there was no data in the disk or the wrapped exporter returned CompletableResultCode.ofFailure().
95-
}
96-
} catch (IOException e) {
97-
// Something unexpected happened.
111+
// Example of reading an exporting spans from disk
112+
OtlpHttpSpanExporter networkExporter;
113+
Iterator<Collection<SpanData>> spanCollections = spanStorage.iterator();
114+
while(spanCollections.hasNext()){
115+
networkExporter.export(spanCollections.next());
98116
}
99117
```
100118

119+
The `File*Storage` iterators delete the previously returned collection when `next()` is called,
120+
assuming that if the next collection is requested is because the previous one was successfully
121+
consumed.
122+
101123
Both the writing and reading processes can run in parallel and they don't overlap
102124
because each is supposed to happen in different files. We ensure that reader and writer don't
103-
accidentally meet in the same file by using the configurable parameters. These parameters set non-overlapping time frames for each action to be done on a single file at a time. On top of that, there's a mechanism in
104-
place to avoid overlapping on edge cases where the time frames ended but the resources haven't been
105-
released. For that mechanism to work properly, this tool assumes that both the reading and the
106-
writing actions are executed within the same application process.
125+
accidentally meet in the same file by using the configurable parameters. These parameters set
126+
non-overlapping time frames for each action to be done on a single file at a time. On top of that,
127+
there's a mechanism in place to avoid overlapping on edge cases where the time frames ended but the
128+
resources haven't been released. For that mechanism to work properly, this tool assumes that both
129+
the reading and the writing actions are executed within the same application process.
107130

108131
## Component owners
109132

-63.7 KB
Loading
-4.84 KB
Loading

disk-buffering/src/main/java/io/opentelemetry/contrib/disk/buffering/LogRecordFromDiskExporter.java

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)