mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 16:10:43 +00:00
## Reports
Both `ZO_CHROME_ENABLED` and `ZO_SMTP_ENABLED` ENV variables need to be
`true` for reports to work.
**Report APIs -**
```
POST /api/{org_id}/reports
PUT /api/{org_id}/reports/{report_name}
GET /api/{org_id}/reports
GET /api/{org_id}/reports/{report_name}
DELETE /api/{org_id}/reports/{report_name}
PUT /api/{org_id}/reports/{report_name}/enable?value=<true|false>
```
**Report body**
```
{
"createdAt": "2024-03-23T09:46:32.913574782Z",
"dashboards": [
{
"dashboard": "7177215109754781699",
"folder": "default",
"tabs": [
"default"
],
"timerange": {
"from": 0,
"period": "1h",
"to": 0,
"type": "relative"
},
"variables": []
}
],
"description": "Report description",
"destinations": [
{
"email": "mail@gmail.com"
}
],
"enabled": false,
"frequency": {
"cron": "",
"interval": 1,
"type": "weeks"
},
"lastEditedBy": "root@example.com",
"mediaType": "pdf",
"message": "Email message",
"name": "report_name",
"orgId": "default",
"owner": "root@example.com",
"password": "ComplexPass#123",
"start": 1711187192913571,
"timezone": "Asia/Calcutta",
"timezoneOffset": 330,
"title": "Email subject",
"user": "root@example.com"
}
```
**ENVs**
| ENV | Description |
|------|---------------|
| `ZO_CHROME_ENABLED` | Must be `true` for reports, Default - `false` |
| `ZO_CHROME_PATH` | If chrome is enabled, custom `chrome` executable
path can be specified. If not specified, it looks for chrome executable
in default locations. If still not found, it automatically downloads a
good known version of `chromium`. |
| `ZO_CHROME_CHECK_DEFAULT_PATH` | If `false`, it does not look for
chromium in default locations, Default is `true`. |
| `ZO_CHROME_DOWNLOAD_PATH` | If chromium can not be found in default
locations and also `ZO_CHROME_PATH` is not specified, it downloads the
system specific chromium in the given path. Default is `./download`
(gitignored). `chromium` is downloaded for the first time only,
afterwords, `chromium` is fetched from the given path. If there is any
error regarding download of `chromium`, delete the download folder as it
might be in a bad state. |
| `ZO_CHROME_NO_SANDBOX` | If `true`, it launches chromium in
`no-sandbox` environment. Default is `false` |
| `ZO_CHROME_SLEEP_SECS` | Specify the number of seconds the
`service::dashboards::reports::generate_report()` will wait to load all
the data in the headless chromium dashboard. Default is `20` seconds. |
| `ZO_CHROME_WINDOW_WIDTH` | Specifies the width of the headless
chromium browser. Default is `1370` |
| `ZO_CHROME_WINDOW_HEIGHT` | Specifies the height of the headless
chromium browser. Default is `730` |
## Scheduler
Currently `Scheduler` is implemented for only `Sqlite`, `Postgres` and
`MySql`.
**API**
```rust
pub trait Scheduler: Sync + Send + 'static {
async fn create_table(&self) -> Result<()>;
async fn create_table_index(&self) -> Result<()>;
async fn push(&self, trigger: Trigger) -> Result<()>;
async fn delete(&self, org: &str, module: TriggerModule, key: &str) -> Result<()>;
async fn update_status(
&self,
org: &str,
module: TriggerModule,
key: &str,
status: TriggerStatus,
retries: i32,
) -> Result<()>;
async fn update_trigger(&self, trigger: Trigger) -> Result<()>;
async fn pull(
&self,
concurrency: i64,
alert_timeout: i64,
report_timeout: i64,
) -> Result<Vec<Trigger>>;
async fn clean_complete(&self, interval: u64);
async fn watch_timeout(&self, interval: u64);
async fn len_module(&self, module: TriggerModule) -> usize;
async fn len(&self) -> usize;
async fn is_empty(&self) -> bool;
async fn clear(&self) -> Result<()>;
}
```
**ENVs**
| ENV | Description |
|------|---------------|
| `ZO_ALERT_SCHEDULE_CONCURRENCY` | The number of `scheduled_jobs` the
`alert_manager` can process at a time. Default is `5` |
| `ZO_ALERT_SCHEDULE_TIMEOUT` | Visibility timeout for alerts. Default
is `90` seconds |
| `ZO_REPORT_SCHEDULE_TIMEOUT` | Visibility timeout for reports. Default
is `300` seconds |
| `ZO_SCHEDULER_MAX_RETRIES` | Maximum number of retries that will be
made for a `scheduled_job`. Default is `3` |
| `ZO_SCHEDULER_CLEAN_INTERVAL` | Time interval after which the
`Scheduler` will clean up the queue. Default is `30` seconds |
| `ZO_SCHEDULER_WATCH_INTERVAL` | Time interval after which the
`Scheduler` will watch for timeout for `scheduled_jobs`. Default is `30`
seconds. |
**Workflow**

280 lines
11 KiB
Rust
280 lines
11 KiB
Rust
// Copyright 2023 Zinc Labs Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use config::{utils::file::set_permission, CONFIG};
|
|
use infra::file_list as infra_file_list;
|
|
|
|
use crate::{
|
|
cli::data::{
|
|
cli::{args as dataArgs, Cli as dataCli},
|
|
export, import, Context,
|
|
},
|
|
common::{infra::config::USERS, meta, migration},
|
|
service::{compact, db, file_list, users},
|
|
};
|
|
|
|
pub async fn cli() -> Result<bool, anyhow::Error> {
|
|
let app = clap::Command::new("openobserve")
|
|
.version(env!("GIT_VERSION"))
|
|
.about(clap::crate_description!())
|
|
.subcommands(&[
|
|
clap::Command::new("reset")
|
|
.about("reset openobserve data")
|
|
.arg(
|
|
clap::Arg::new("component")
|
|
.short('c')
|
|
.long("component")
|
|
.help(
|
|
"reset data of the component: root, user, alert, dashboard, function, stream-stats",
|
|
),
|
|
),
|
|
clap::Command::new("import")
|
|
.about("import openobserve data").args(dataArgs()),
|
|
clap::Command::new("export")
|
|
.about("export openobserve data").args(dataArgs()),
|
|
clap::Command::new("view")
|
|
.about("view openobserve data")
|
|
.arg(
|
|
clap::Arg::new("component")
|
|
.short('c')
|
|
.long("component")
|
|
.help("view data of the component: version, user"),
|
|
),
|
|
clap::Command::new("init-dir")
|
|
.about("init openobserve data dir")
|
|
.arg(
|
|
clap::Arg::new("path")
|
|
.short('p')
|
|
.long("path")
|
|
.help("init this path as data root dir"),
|
|
),
|
|
clap::Command::new("migrate-file-list")
|
|
.about("migrate file-list")
|
|
.args([
|
|
clap::Arg::new("prefix")
|
|
.short('p')
|
|
.long("prefix")
|
|
.value_name("prefix")
|
|
.required(false)
|
|
.help("only migrate specified prefix, default is all"),
|
|
clap::Arg::new("from")
|
|
.short('f')
|
|
.long("from")
|
|
.value_name("from")
|
|
.required(true)
|
|
.help("migrate from: sled, sqlite, etcd, mysql, postgresql"),
|
|
clap::Arg::new("to")
|
|
.short('t')
|
|
.long("to")
|
|
.value_name("to")
|
|
.required(true)
|
|
.help("migrate to: sqlite, mysql, postgresql"),
|
|
]),
|
|
clap::Command::new("migrate-meta")
|
|
.about("migrate meta")
|
|
.args([
|
|
clap::Arg::new("from")
|
|
.short('f')
|
|
.long("from")
|
|
.value_name("from")
|
|
.required(true)
|
|
.help("migrate from: sled, sqlite, etcd, mysql, postgresql"),
|
|
clap::Arg::new("to")
|
|
.short('t')
|
|
.long("to")
|
|
.value_name("to")
|
|
.required(true)
|
|
.help("migrate to: sqlite, etcd, mysql, postgresql"),
|
|
]),
|
|
clap::Command::new("migrate-dashboards").about("migrate-dashboards"),
|
|
clap::Command::new("delete-parquet")
|
|
.about("delete parquet files from s3 and file_list")
|
|
.arg(
|
|
clap::Arg::new("file")
|
|
.short('f')
|
|
.long("file")
|
|
.value_name("file")
|
|
.help("the parquet file name"),
|
|
),
|
|
clap::Command::new("migrate-schemas").about("migrate from single row to row per schema version"),
|
|
])
|
|
.get_matches();
|
|
|
|
if app.subcommand().is_none() {
|
|
return Ok(false);
|
|
}
|
|
|
|
env_logger::init_from_env(env_logger::Env::new().default_filter_or("INFO"));
|
|
|
|
let (name, command) = app.subcommand().unwrap();
|
|
if name == "init-dir" {
|
|
match command.get_one::<String>("path") {
|
|
Some(path) => {
|
|
set_permission(path, 0o777)?;
|
|
println!("init dir {} succeeded", path);
|
|
}
|
|
None => {
|
|
return Err(anyhow::anyhow!("please set data path"));
|
|
}
|
|
}
|
|
return Ok(true);
|
|
}
|
|
|
|
// init infra, create data dir & tables
|
|
infra::init().await.expect("infra init failed");
|
|
match name {
|
|
"reset" => {
|
|
let component = command.get_one::<String>("component").unwrap();
|
|
match component.as_str() {
|
|
"root" => {
|
|
let _ = users::update_user(
|
|
meta::organization::DEFAULT_ORG,
|
|
CONFIG.auth.root_user_email.as_str(),
|
|
false,
|
|
CONFIG.auth.root_user_email.as_str(),
|
|
meta::user::UpdateUser {
|
|
change_password: true,
|
|
old_password: None,
|
|
new_password: Some(CONFIG.auth.root_user_password.clone()),
|
|
role: Some(meta::user::UserRole::Root),
|
|
first_name: Some("root".to_owned()),
|
|
last_name: Some("".to_owned()),
|
|
token: None,
|
|
},
|
|
)
|
|
.await?;
|
|
}
|
|
"user" => {
|
|
db::user::reset().await?;
|
|
}
|
|
"alert" => {
|
|
db::alerts::reset().await?;
|
|
}
|
|
"dashboard" => {
|
|
db::dashboards::reset().await?;
|
|
}
|
|
"report" => {
|
|
db::dashboards::reports::reset().await?;
|
|
}
|
|
"function" => {
|
|
db::functions::reset().await?;
|
|
}
|
|
"stream-stats" => {
|
|
// reset stream stats update offset
|
|
db::compact::stats::set_offset(0, None).await?;
|
|
// reset stream stats table data
|
|
infra_file_list::reset_stream_stats().await?;
|
|
// load stream list
|
|
db::schema::cache().await?;
|
|
// update stats from file list
|
|
compact::stats::update_stats_from_file_list()
|
|
.await
|
|
.expect("file list remote calculate stats failed");
|
|
}
|
|
_ => {
|
|
return Err(anyhow::anyhow!("unsupport reset component: {}", component));
|
|
}
|
|
}
|
|
}
|
|
"view" => {
|
|
let component = command.get_one::<String>("component").unwrap();
|
|
match component.as_str() {
|
|
"version" => {
|
|
println!("version: {}", db::version::get().await?);
|
|
}
|
|
"user" => {
|
|
db::user::cache().await?;
|
|
let mut id = 0;
|
|
for user in USERS.iter() {
|
|
id += 1;
|
|
println!("{id}\t{:?}\n{:?}", user.key(), user.value());
|
|
}
|
|
}
|
|
_ => {
|
|
return Err(anyhow::anyhow!("unsupport reset component: {component}"));
|
|
}
|
|
}
|
|
}
|
|
"migrate-file-list" => {
|
|
let prefix = match command.get_one::<String>("prefix") {
|
|
Some(prefix) => prefix.to_string(),
|
|
None => "".to_string(),
|
|
};
|
|
let from = match command.get_one::<String>("from") {
|
|
Some(from) => from.to_string(),
|
|
None => "".to_string(),
|
|
};
|
|
let to = match command.get_one::<String>("to") {
|
|
Some(to) => to.to_string(),
|
|
None => "".to_string(),
|
|
};
|
|
println!(
|
|
"Running migration file_list from {} to {}, with prefix: {}",
|
|
from, to, prefix
|
|
);
|
|
migration::file_list::run(&prefix, &from, &to).await?;
|
|
}
|
|
"migrate-meta" => {
|
|
let from = match command.get_one::<String>("from") {
|
|
Some(from) => from.to_string(),
|
|
None => "".to_string(),
|
|
};
|
|
let to = match command.get_one::<String>("to") {
|
|
Some(to) => to.to_string(),
|
|
None => "".to_string(),
|
|
};
|
|
println!("Running migration metadata from {} to {}", from, to);
|
|
migration::meta::run(&from, &to).await?
|
|
}
|
|
"migrate-dashboards" => {
|
|
println!("Running migration dashboard");
|
|
migration::dashboards::run().await?
|
|
}
|
|
"delete-parquet" => {
|
|
let file = command.get_one::<String>("file").unwrap();
|
|
match file_list::delete_parquet_file(file, true).await {
|
|
Ok(_) => {
|
|
println!("delete parquet file {} succeeded", file);
|
|
}
|
|
Err(e) => {
|
|
println!("delete parquet file {} failed, error: {}", file, e);
|
|
}
|
|
}
|
|
}
|
|
"import" => {
|
|
return import::Import::operator(dataCli::arg_matches(command.clone()));
|
|
}
|
|
"export" => {
|
|
return export::Export::operator(dataCli::arg_matches(command.clone()));
|
|
}
|
|
"migrate-schemas" => {
|
|
println!("Running schema migration to row per schema version");
|
|
migration::schema::run().await?
|
|
}
|
|
_ => {
|
|
return Err(anyhow::anyhow!("unsupport sub command: {name}"));
|
|
}
|
|
}
|
|
|
|
// flush db
|
|
let db = infra::db::get_db().await;
|
|
if let Err(e) = db.close().await {
|
|
log::error!("waiting for db close failed, error: {}", e);
|
|
}
|
|
|
|
println!("command {name} execute succeeded");
|
|
Ok(true)
|
|
}
|