mirror of
https://github.com/OMGeeky/appmap_tracing_test.git
synced 2025-12-26 16:07:50 +01:00
Initial attempt to create Appmaps (currently manually but the supporting data structure is there)
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
/.idea
|
||||
/maps
|
||||
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "appmap_tracing_test"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
|
||||
serde = { version = "1.0", features=["derive", "default"] }
|
||||
serde_json = "1.0"
|
||||
3
appmap.yml
Normal file
3
appmap.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
appmap_dir: maps/tmp/appmap
|
||||
language: rust
|
||||
name: appmap_tracing_test
|
||||
254
src/appmap_definition.rs
Normal file
254
src/appmap_definition.rs
Normal file
@@ -0,0 +1,254 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use event_id::EventId;
|
||||
|
||||
use crate::appmap_definition::event_id::ObjectId;
|
||||
|
||||
//region todo objects
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct MetadataObject {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct ExceptionReturnObject {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct HttpServerRequestCallObject {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct HttpServerResponseCallObject {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct HttpClientRequestCallObject {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct HttpClientResponseCallObject {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct SqlQueryCallObject {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct MessageCallObject {}
|
||||
//endregion
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct AppMapObject {
|
||||
pub version: String,
|
||||
pub metadata: MetadataObject,
|
||||
// pub class_map: Vec<CodeObject>,
|
||||
pub class_map: Vec<CodeObjectType>,
|
||||
pub events: Vec<EventObject>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub event_updates: Option<HashMap<u32, EventObject>>,
|
||||
}
|
||||
//region events
|
||||
mod event_id;
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct EventObject {
|
||||
//region common
|
||||
///Required unique identifier. Example: 23522.
|
||||
pub id: EventId,
|
||||
///Required identifier of the execution thread. Example: 70340688724000.
|
||||
pub thread_id: u32,
|
||||
//endregion
|
||||
#[serde(flatten)]
|
||||
pub event: EventObjectType,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[serde(tag = "event")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum EventObjectType {
|
||||
Call(CallObject),
|
||||
Return(ReturnObject),
|
||||
}
|
||||
//region Return Objects
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct ReturnObject {
|
||||
///Required id of the "call" event corresponding to this "return".
|
||||
parent_id: u32,
|
||||
///Optional elapsed time in seconds of this function call.
|
||||
elapsed: Option<usize>,
|
||||
#[serde(flatten)]
|
||||
data: ReturnObjectType,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum ReturnObjectType {
|
||||
Normal,
|
||||
Function(FunctionReturnObject),
|
||||
Exception(ExceptionReturnObject),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct FunctionReturnObject {
|
||||
/// Optional object describing the return value. If present, this value uses parameter object format.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub return_value: Option<ParameterObject>,
|
||||
/// Optional array of exceptions causing this method to exit. If present, this value uses exception
|
||||
/// object format. When an exception is a wrapper for an underlying cause, the cause is the next
|
||||
/// exception in the exceptions array.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub exceptions: Option<Vec<ExceptionObject>>,
|
||||
}
|
||||
//endregion
|
||||
//region call objects
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct CallObject {
|
||||
///Required name of the class which defines the method. Example: "MyApp::User".
|
||||
pub defined_class: String,
|
||||
///Required name of the function which was called in this event. Example: "show".
|
||||
pub method_id: String,
|
||||
/// Recommended path name of the file which triggered the event. Example: "/src/architecture/lib/appland/local/client.rb".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub path: Option<PathBuf>,
|
||||
///Recommended line number which triggered the event. Example: 5.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub lineno: Option<usize>,
|
||||
///Optional parameter object describing the object on which the function is called. Corresponds to the receiver, self and this concept found in various programming languages.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub receiver: Option<ParameterObject>,
|
||||
///Recommended array of parameter objects describing the function call parameters.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub parameters: Option<Vec<ParameterObject>>,
|
||||
///Required flag if the method is class-scoped (static) or instance-scoped. Must be true or false. Example: true.
|
||||
pub is_static: bool,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub data: CallObjectType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum CallObjectType {
|
||||
Normal,
|
||||
Function(FunctionCallObject),
|
||||
HttpServerRequest(HttpServerRequestCallObject),
|
||||
HttpServerResponse(HttpServerResponseCallObject),
|
||||
HttpClientRequest(HttpClientRequestCallObject),
|
||||
HttpClientResponse(HttpClientResponseCallObject),
|
||||
SqlQuery(SqlQueryCallObject),
|
||||
Message(MessageCallObject),
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
/// Note:
|
||||
///
|
||||
/// In order to correlate function call events with function objects defined in the class map, the
|
||||
/// path and lineno attributes of each "call" event should exactly match the location attribute of
|
||||
/// the corresponding function in the classMap.
|
||||
pub struct FunctionCallObject {
|
||||
/// Recommended path name of the file which triggered the event.
|
||||
/// Example: "/src/architecture/lib/appland/local/client.rb".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub path: Option<PathBuf>,
|
||||
/// Recommended line number which triggered the event. Example: 5.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub lineno: Option<usize>,
|
||||
/// Optional parameter object describing the object on which the function is called. Corresponds
|
||||
/// to the receiver, self and this concept found in various programming languages.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub receiver: Option<ParameterObject>,
|
||||
/// Recommended array of parameter objects describing the function call parameters.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub parameters: Option<Vec<ParameterObject>>,
|
||||
}
|
||||
//endregion
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct ParameterObject {
|
||||
///
|
||||
///
|
||||
///Recommended name of the parameter. Example: "login".
|
||||
name: Option<String>,
|
||||
/// Recommended unique id of the object. Example: 70340693307040
|
||||
object_id: Option<ObjectId>,
|
||||
///Required fully qualified class or type name of the object. Example: "MyApp::User".
|
||||
class: String,
|
||||
///Required string describing the object. This is not a strict JSON serialization, but rather a display string which is intended for the user. These strings should be trimmed in length to 100 characters. Example: "MyApp user 'alice'"
|
||||
value: String,
|
||||
/// Recommended number of elements in an array or hash object. Example. "5".
|
||||
size: Option<usize>,
|
||||
/// Optional schema indicating property names and types of hash and hash-like objects. Each entry is a name, class and optional nested properties or items.
|
||||
properties: Option<Vec<PropertiesObject>>,
|
||||
/// Optional schema indicating element types of array and array-like objects. Each entry is a class and optional nested properties or items.
|
||||
items: Option<Vec<ItemObject>>,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct PropertiesObject {
|
||||
pub name: String,
|
||||
pub class: String,
|
||||
pub properties: Option<Vec<PropertiesObject>>,
|
||||
pub items: Option<Vec<ItemObject>>,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct ItemObject {
|
||||
pub class: String,
|
||||
pub properties: Option<Vec<PropertiesObject>>,
|
||||
pub items: Option<Vec<ItemObject>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct ExceptionObject {
|
||||
pub class: String,
|
||||
pub message: String,
|
||||
pub object_id: ObjectId,
|
||||
pub path: Option<PathBuf>,
|
||||
pub lineno: Option<usize>,
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region class_map
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct CodeObject {
|
||||
pub name: String,
|
||||
pub ty: CodeObjectType,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum CodeObjectType {
|
||||
Package(PackageCodeObject),
|
||||
Class(ClassCodeObject),
|
||||
Function(FunctionCodeObject),
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct PackageCodeObject {
|
||||
pub name: String,
|
||||
pub children: Option<Vec<CodeObjectType>>,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct ClassCodeObject {
|
||||
pub name: String,
|
||||
pub children: Option<Vec<CodeObjectType>>,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct FunctionCodeObject {
|
||||
pub name: String,
|
||||
///Recommended File path and line number, separated by a colon. Example: "/Users/alice/src/myapp/lib/myapp/main.rb:5".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub location: Option<String>,
|
||||
///Required flag if the method is class-scoped (static) or instance-scoped. Must be true or false. Example: true.
|
||||
pub is_static: bool,
|
||||
///Optional list of arbitrary labels describing the function.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub labels: Option<Vec<String>>,
|
||||
///Optional documentation comment for the function extracted from the source code.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub comment: Option<String>,
|
||||
///Optional verbatim source code of the function.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub source: Option<String>,
|
||||
}
|
||||
|
||||
//endregion
|
||||
39
src/appmap_definition/event_id.rs
Normal file
39
src/appmap_definition/event_id.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct EventId(u32);
|
||||
impl Deref for EventId {
|
||||
type Target = u32;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl DerefMut for EventId {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for EventId {
|
||||
fn from(value: u32) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ObjectId(u64);
|
||||
impl Deref for ObjectId {
|
||||
type Target = u64;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl DerefMut for ObjectId {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
16
src/lib.rs
Normal file
16
src/lib.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::time::Instant;
|
||||
use tracing::field::{Field, Visit};
|
||||
use tracing::span::{Attributes, Record};
|
||||
use tracing::{Id, Subscriber};
|
||||
use tracing_subscriber::layer::Context;
|
||||
use tracing_subscriber::Layer;
|
||||
pub mod appmap_definition;
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct AppMap {
|
||||
#[serde(flatten)]
|
||||
pub data: crate::appmap_definition::AppMapObject,
|
||||
}
|
||||
78
src/main.rs
Normal file
78
src/main.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fmt::Error as FmtError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use appmap_tracing_test::appmap_definition::{
|
||||
AppMapObject, CallObject, CallObjectType, ClassCodeObject, CodeObject, CodeObjectType, EventId,
|
||||
EventObject, EventObjectType, FunctionCallObject, FunctionCodeObject, MetadataObject,
|
||||
PackageCodeObject,
|
||||
};
|
||||
use appmap_tracing_test::AppMap;
|
||||
|
||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||
sample_json()?;
|
||||
Ok(())
|
||||
}
|
||||
fn sample_json() -> Result<(), Box<dyn Error>> {
|
||||
let data = AppMap {
|
||||
data: AppMapObject {
|
||||
metadata: MetadataObject {},
|
||||
class_map: vec![CodeObjectType::Package(PackageCodeObject {
|
||||
name: "main pkg".to_string(),
|
||||
children: Some(vec![CodeObjectType::Class(ClassCodeObject {
|
||||
name: "main cls".to_string(),
|
||||
children: Some(vec![CodeObjectType::Function(FunctionCodeObject {
|
||||
name: "sample_json".to_string(),
|
||||
location: Some(format!(
|
||||
"{}:{}",
|
||||
PathBuf::from("src/main.rs").to_str().ok_or(FmtError)?,
|
||||
14
|
||||
)),
|
||||
is_static: true,
|
||||
labels: Some(vec!["security".to_string()]),
|
||||
comment: None,
|
||||
source: None,
|
||||
})]),
|
||||
})]),
|
||||
})],
|
||||
events: vec![EventObject {
|
||||
id: EventId::from(1),
|
||||
thread_id: 9999,
|
||||
event: EventObjectType::Call(CallObject {
|
||||
defined_class: "main".to_string(),
|
||||
method_id: "sample_json".to_string(),
|
||||
path: Some(PathBuf::from("src/main.rs")),
|
||||
lineno: Some(14),
|
||||
receiver: None,
|
||||
parameters: None,
|
||||
is_static: true,
|
||||
data: CallObjectType::Function(FunctionCallObject {
|
||||
path: None,
|
||||
lineno: None,
|
||||
receiver: None,
|
||||
parameters: None,
|
||||
}),
|
||||
}),
|
||||
}],
|
||||
version: String::from("1.12"),
|
||||
event_updates: None,
|
||||
},
|
||||
};
|
||||
|
||||
println!("data debug: {:?}", data);
|
||||
let data_string = serde_json::to_string_pretty(&data)?;
|
||||
println!("data to string: {}", data_string);
|
||||
|
||||
let data_reversed: AppMap = sample_from_str(&data_string)?;
|
||||
println!("data_reversed debug: {:?}", data_reversed);
|
||||
assert_eq!(data, data_reversed);
|
||||
println!("it works!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn sample_from_str(s: &str) -> Result<AppMap, Box<dyn Error>> {
|
||||
let data_reversed: AppMap = serde_json::from_str(s)?;
|
||||
println!("sample_from_str(s): result debug: {:?}", data_reversed);
|
||||
Ok(data_reversed)
|
||||
}
|
||||
Reference in New Issue
Block a user