Skip to content

Commit f4f62df

Browse files
add system defined tags and free form labels to datasets
PUT /api/v1/logstream/{name} accepts X-P-Dataset-Tags and X-P-Dataset-Labels headers (comma-separated) on stream creation PUT /api/prism/v1/datasets/{name} - update tags and labels GET /api/prism/v1/datasets/{name}/correlated - find datasets sharing tags or labels GET /api/prism/v1/datasets/tags/{tag} - find all datasets with a specific tag include tags and labels in home api response
1 parent a1621d3 commit f4f62df

14 files changed

Lines changed: 209 additions & 63 deletions

File tree

src/connectors/kafka/processor.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ impl ParseableSinkProcessor {
6464
vec![log_source_entry],
6565
TelemetryType::default(),
6666
tenant_id,
67-
None,
67+
vec![],
68+
vec![],
6869
)
6970
.await?;
7071

src/handlers/http/ingest.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ pub async fn ingest(
120120
vec![log_source_entry.clone()],
121121
telemetry_type,
122122
&tenant_id,
123-
None,
123+
vec![],
124+
vec![],
124125
)
125126
.await
126127
.map_err(|e| {
@@ -239,7 +240,8 @@ pub async fn setup_otel_stream(
239240
vec![log_source_entry.clone()],
240241
telemetry_type,
241242
&tenant_id,
242-
None,
243+
vec![],
244+
vec![],
243245
)
244246
.await?;
245247
let mut time_partition = None;

src/handlers/http/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod about;
3232
pub mod alerts;
3333
pub mod cluster;
3434
pub mod correlation;
35+
pub mod datasets;
3536
pub mod demo_data;
3637
pub mod health_check;
3738
pub mod ingest;

src/handlers/http/modal/server.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,39 @@ impl Server {
200200
}
201201

202202
pub fn get_prism_datasets() -> Scope {
203-
web::scope("/datasets").route(
204-
"",
205-
web::post()
206-
.to(http::prism_logstream::post_datasets)
207-
.authorize_for_resource(Action::GetStreamInfo)
208-
.authorize_for_resource(Action::GetStats)
209-
.authorize_for_resource(Action::GetRetention),
210-
)
203+
web::scope("/datasets")
204+
.route(
205+
"",
206+
web::post()
207+
.to(http::prism_logstream::post_datasets)
208+
.authorize_for_resource(Action::GetStreamInfo)
209+
.authorize_for_resource(Action::GetStats)
210+
.authorize_for_resource(Action::GetRetention),
211+
)
212+
.route(
213+
"/tags/{tag}",
214+
web::get()
215+
.to(http::datasets::get_datasets_by_tag)
216+
.authorize_for_resource(Action::GetStreamInfo),
217+
)
218+
.route(
219+
"/{name}/correlated",
220+
web::get()
221+
.to(http::datasets::get_correlated_datasets)
222+
.authorize_for_resource(Action::GetStreamInfo),
223+
)
224+
.route(
225+
"/{name}/tags",
226+
web::put()
227+
.to(http::datasets::put_dataset_tags)
228+
.authorize_for_resource(Action::CreateStream),
229+
)
230+
.route(
231+
"/{name}/labels",
232+
web::put()
233+
.to(http::datasets::put_dataset_labels)
234+
.authorize_for_resource(Action::CreateStream),
235+
)
211236
}
212237

213238
pub fn get_demo_data_webscope() -> Scope {

src/handlers/http/modal/utils/logstream_utils.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,18 @@
1616
*
1717
*/
1818

19+
use actix_web::http::header::HeaderMap;
20+
1921
use crate::{
2022
event::format::LogSource,
2123
handlers::{
22-
CUSTOM_PARTITION_KEY, DATASET_TAG_KEY, DatasetTag, LOG_SOURCE_KEY, STATIC_SCHEMA_FLAG,
23-
STREAM_TYPE_KEY, TELEMETRY_TYPE_KEY, TIME_PARTITION_KEY, TIME_PARTITION_LIMIT_KEY,
24-
TelemetryType, UPDATE_STREAM_KEY,
24+
CUSTOM_PARTITION_KEY, DATASET_LABELS_KEY, DATASET_TAG_KEY, DATASET_TAGS_KEY, DatasetTag,
25+
LOG_SOURCE_KEY, STATIC_SCHEMA_FLAG, STREAM_TYPE_KEY, TELEMETRY_TYPE_KEY,
26+
TIME_PARTITION_KEY, TIME_PARTITION_LIMIT_KEY, TelemetryType, UPDATE_STREAM_KEY,
27+
parse_dataset_labels, parse_dataset_tags,
2528
},
2629
storage::StreamType,
2730
};
28-
use actix_web::http::header::HeaderMap;
29-
use tracing::warn;
3031

3132
#[derive(Debug, Default)]
3233
pub struct PutStreamHeaders {
@@ -38,7 +39,8 @@ pub struct PutStreamHeaders {
3839
pub stream_type: StreamType,
3940
pub log_source: LogSource,
4041
pub telemetry_type: TelemetryType,
41-
pub dataset_tag: Option<DatasetTag>,
42+
pub dataset_tags: Vec<DatasetTag>,
43+
pub dataset_labels: Vec<String>,
4244
}
4345

4446
impl From<&HeaderMap> for PutStreamHeaders {
@@ -72,16 +74,17 @@ impl From<&HeaderMap> for PutStreamHeaders {
7274
.get(TELEMETRY_TYPE_KEY)
7375
.and_then(|v| v.to_str().ok())
7476
.map_or(TelemetryType::Logs, TelemetryType::from),
75-
dataset_tag: headers
76-
.get(DATASET_TAG_KEY)
77+
dataset_tags: headers
78+
.get(DATASET_TAGS_KEY)
79+
.or_else(|| headers.get(DATASET_TAG_KEY))
7780
.and_then(|v| v.to_str().ok())
78-
.and_then(|v| match DatasetTag::try_from(v) {
79-
Ok(tag) => Some(tag),
80-
Err(err) => {
81-
warn!("Invalid dataset tag '{v}': {err}");
82-
None
83-
}
84-
}),
81+
.map(parse_dataset_tags)
82+
.unwrap_or_default(),
83+
dataset_labels: headers
84+
.get(DATASET_LABELS_KEY)
85+
.and_then(|v| v.to_str().ok())
86+
.map(parse_dataset_labels)
87+
.unwrap_or_default(),
8588
}
8689
}
8790
}

src/handlers/mod.rs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
*
1717
*/
1818

19+
use std::collections::HashSet;
1920
use std::fmt::Display;
2021

2122
use serde::{Deserialize, Serialize};
23+
use tracing::warn;
2224

2325
pub mod airplane;
2426
pub mod http;
@@ -36,6 +38,8 @@ pub const UPDATE_STREAM_KEY: &str = "x-p-update-stream";
3638
pub const STREAM_TYPE_KEY: &str = "x-p-stream-type";
3739
pub const TELEMETRY_TYPE_KEY: &str = "x-p-telemetry-type";
3840
pub const DATASET_TAG_KEY: &str = "x-p-dataset-tag";
41+
pub const DATASET_TAGS_KEY: &str = "x-p-dataset-tags";
42+
pub const DATASET_LABELS_KEY: &str = "x-p-dataset-labels";
3943
pub const TENANT_ID: &str = "x-p-tenant";
4044
const COOKIE_AGE_DAYS: usize = 7;
4145
const SESSION_COOKIE_NAME: &str = "session";
@@ -85,24 +89,28 @@ impl Display for TelemetryType {
8589
}
8690

8791
/// Tag for categorizing datasets/streams by observability domain
88-
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
92+
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
8993
#[serde(rename_all = "kebab-case")]
9094
pub enum DatasetTag {
91-
AgentObservability,
92-
K8sObservability,
95+
AgentMonitoring,
96+
K8sMonitoring,
9397
DatabaseObservability,
98+
ApplicationMonitoring,
99+
ServiceMap,
94100
}
95101

96102
impl TryFrom<&str> for DatasetTag {
97103
type Error = &'static str;
98104

99105
fn try_from(s: &str) -> Result<Self, Self::Error> {
100106
match s.to_lowercase().as_str() {
101-
"agent-observability" => Ok(DatasetTag::AgentObservability),
102-
"k8s-observability" => Ok(DatasetTag::K8sObservability),
107+
"agent-monitoring" => Ok(DatasetTag::AgentMonitoring),
108+
"k8s-monitoring" => Ok(DatasetTag::K8sMonitoring),
103109
"database-observability" => Ok(DatasetTag::DatabaseObservability),
110+
"application-monitoring" => Ok(DatasetTag::ApplicationMonitoring),
111+
"service-map" => Ok(DatasetTag::ServiceMap),
104112
_ => Err(
105-
"Invalid dataset tag. Supported values: agent-observability, k8s-observability, database-observability",
113+
"Invalid dataset tag. Supported values: agent-monitoring, k8s-monitoring, database-observability, application-monitoring, service-map",
106114
),
107115
}
108116
}
@@ -111,9 +119,43 @@ impl TryFrom<&str> for DatasetTag {
111119
impl Display for DatasetTag {
112120
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113121
f.write_str(match self {
114-
DatasetTag::AgentObservability => "agent-observability",
115-
DatasetTag::K8sObservability => "k8s-observability",
122+
DatasetTag::AgentMonitoring => "agent-monitoring",
123+
DatasetTag::K8sMonitoring => "k8s-monitoring",
116124
DatasetTag::DatabaseObservability => "database-observability",
125+
DatasetTag::ApplicationMonitoring => "application-monitoring",
126+
DatasetTag::ServiceMap => "service-map",
117127
})
118128
}
119129
}
130+
131+
pub fn parse_dataset_tags(header_value: &str) -> Vec<DatasetTag> {
132+
header_value
133+
.split(',')
134+
.filter_map(|s| {
135+
let trimmed = s.trim();
136+
if trimmed.is_empty() {
137+
None
138+
} else {
139+
match DatasetTag::try_from(trimmed) {
140+
Ok(tag) => Some(tag),
141+
Err(err) => {
142+
warn!("Invalid dataset tag '{trimmed}': {err}");
143+
None
144+
}
145+
}
146+
}
147+
})
148+
.collect::<HashSet<_>>()
149+
.into_iter()
150+
.collect()
151+
}
152+
153+
pub fn parse_dataset_labels(header_value: &str) -> Vec<String> {
154+
header_value
155+
.split(',')
156+
.map(|s| s.trim().to_string())
157+
.filter(|s| !s.is_empty())
158+
.collect::<HashSet<_>>()
159+
.into_iter()
160+
.collect()
161+
}

src/metadata.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ pub struct LogStreamMetadata {
9393
pub stream_type: StreamType,
9494
pub log_source: Vec<LogSourceEntry>,
9595
pub telemetry_type: TelemetryType,
96-
pub dataset_tag: Option<DatasetTag>,
96+
pub dataset_tags: Vec<DatasetTag>,
97+
pub dataset_labels: Vec<String>,
9798
}
9899

99100
impl LogStreamMetadata {
@@ -109,7 +110,8 @@ impl LogStreamMetadata {
109110
schema_version: SchemaVersion,
110111
log_source: Vec<LogSourceEntry>,
111112
telemetry_type: TelemetryType,
112-
dataset_tag: Option<DatasetTag>,
113+
dataset_tags: Vec<DatasetTag>,
114+
dataset_labels: Vec<String>,
113115
) -> Self {
114116
LogStreamMetadata {
115117
created_at: if created_at.is_empty() {
@@ -134,7 +136,8 @@ impl LogStreamMetadata {
134136
schema_version,
135137
log_source,
136138
telemetry_type,
137-
dataset_tag,
139+
dataset_tags,
140+
dataset_labels,
138141
..Default::default()
139142
}
140143
}

src/migration/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,8 @@ async fn setup_logstream_metadata(
454454
stream_type,
455455
log_source,
456456
telemetry_type,
457-
dataset_tag,
457+
dataset_tags,
458+
dataset_labels,
458459
..
459460
} = serde_json::from_value(stream_metadata_value).unwrap_or_default();
460461

@@ -500,7 +501,8 @@ async fn setup_logstream_metadata(
500501
stream_type,
501502
log_source,
502503
telemetry_type,
503-
dataset_tag,
504+
dataset_tags,
505+
dataset_labels,
504506
};
505507

506508
Ok(metadata)

src/parseable/mod.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,8 @@ impl Parseable {
416416
let schema_version = stream_metadata.schema_version;
417417
let log_source = stream_metadata.log_source;
418418
let telemetry_type = stream_metadata.telemetry_type;
419-
let dataset_tag = stream_metadata.dataset_tag;
419+
let dataset_tags = stream_metadata.dataset_tags;
420+
let dataset_labels = stream_metadata.dataset_labels;
420421
let mut metadata = LogStreamMetadata::new(
421422
created_at,
422423
time_partition,
@@ -428,7 +429,8 @@ impl Parseable {
428429
schema_version,
429430
log_source,
430431
telemetry_type,
431-
dataset_tag,
432+
dataset_tags,
433+
dataset_labels,
432434
);
433435

434436
// Set hot tier fields from the stored metadata
@@ -533,7 +535,8 @@ impl Parseable {
533535
log_source: Vec<LogSourceEntry>,
534536
telemetry_type: TelemetryType,
535537
tenant_id: &Option<String>,
536-
dataset_tag: Option<DatasetTag>,
538+
dataset_tags: Vec<DatasetTag>,
539+
dataset_labels: Vec<String>,
537540
) -> Result<bool, PostError> {
538541
if self.streams.contains(stream_name, tenant_id) {
539542
return Ok(true);
@@ -566,7 +569,8 @@ impl Parseable {
566569
log_source,
567570
telemetry_type,
568571
tenant_id,
569-
dataset_tag,
572+
dataset_tags,
573+
dataset_labels,
570574
)
571575
.await?;
572576

@@ -643,7 +647,8 @@ impl Parseable {
643647
stream_type,
644648
log_source,
645649
telemetry_type,
646-
dataset_tag,
650+
dataset_tags,
651+
dataset_labels,
647652
} = headers.into();
648653

649654
let stream_in_memory_dont_update =
@@ -717,7 +722,8 @@ impl Parseable {
717722
vec![log_source_entry],
718723
telemetry_type,
719724
tenant_id,
720-
dataset_tag,
725+
dataset_tags,
726+
dataset_labels,
721727
)
722728
.await?;
723729

@@ -779,7 +785,8 @@ impl Parseable {
779785
log_source: Vec<LogSourceEntry>,
780786
telemetry_type: TelemetryType,
781787
tenant_id: &Option<String>,
782-
dataset_tag: Option<DatasetTag>,
788+
dataset_tags: Vec<DatasetTag>,
789+
dataset_labels: Vec<String>,
783790
) -> Result<(), CreateStreamError> {
784791
// fail to proceed if invalid stream name
785792
if stream_type != StreamType::Internal {
@@ -804,7 +811,8 @@ impl Parseable {
804811
},
805812
log_source: log_source.clone(),
806813
telemetry_type,
807-
dataset_tag,
814+
dataset_tags: dataset_tags.clone(),
815+
dataset_labels: dataset_labels.clone(),
808816
..Default::default()
809817
};
810818

@@ -834,7 +842,8 @@ impl Parseable {
834842
SchemaVersion::V1, // New stream
835843
log_source,
836844
telemetry_type,
837-
dataset_tag,
845+
dataset_tags,
846+
dataset_labels,
838847
);
839848
let ingestor_id = INGESTOR_META
840849
.get()

0 commit comments

Comments
 (0)