Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { QueryDefinition } from '@perses-dev/core';
import { parseVariables } from '@perses-dev/plugin-system';
import { getClickHouseLogData } from './get-click-house-log-data';
import { ClickHouseLogQueryEditor } from './ClickHouseLogQueryEditor';
Expand All @@ -28,4 +29,18 @@ export const ClickHouseLogQuery: LogQueryPlugin<ClickHouseLogQuerySpec> = {
variables: allVariables,
};
},
createVolumeQuery: (spec: ClickHouseLogQuerySpec): QueryDefinition | null => {
Copy link
Member

@Gladorme Gladorme Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will need to be added LogQueryPlugin interface too, so it's easier to use it everywhere and future Log plugins, know it's existing.

// Only create volume query if there's a valid query
if (!spec.query || !spec.query.trim()) {
return null;
}

// For ClickHouse, we need to transform the SQL query into a volume aggregation
// This is a simplified approach that may need refinement based on actual query patterns
// For now, returning null to indicate volume queries need more sophisticated parsing
// TODO: Implement proper SQL parsing and transformation for volume queries
// Example target: SELECT toStartOfInterval(timestamp, INTERVAL 1 minute) as time, level, count() FROM ... GROUP BY time, level

return null;
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { AbsoluteTimeRange, UnknownSpec, LogData } from '@perses-dev/core';
import { AbsoluteTimeRange, UnknownSpec, LogData, QueryDefinition } from '@perses-dev/core';
import { DatasourceStore, Plugin, VariableStateMap } from '@perses-dev/plugin-system';

export interface LogQueryResult {
Expand All @@ -35,4 +35,10 @@ type LogQueryPluginDependencies = {
export interface LogQueryPlugin<Spec = UnknownSpec> extends Plugin<Spec> {
getLogData: (spec: Spec, ctx: ClickHouseQueryContext) => Promise<LogQueryResult>;
dependsOn?: (spec: Spec, ctx: ClickHouseQueryContext) => LogQueryPluginDependencies;
/**
* Optional method to create a TimeSeriesQuery for log volume visualization.
* Returns a QueryDefinition that aggregates log volumes over time, typically grouped by log level.
* Returns null if volume queries are not supported or cannot be generated from the given spec.
*/
createVolumeQuery?: (spec: Spec, ctx: ClickHouseQueryContext) => QueryDefinition | null;
}
20 changes: 20 additions & 0 deletions logexplorer/.cjs.swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"target": "es2022",
"transform": {
"react": {
"runtime": "automatic",
"useBuiltins": true
}
}
},
"module": {
"type": "commonjs"
},
"exclude": ["\\.(stories|test)\\."]
}
21 changes: 21 additions & 0 deletions logexplorer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.idea/

# Local
.DS_Store
*.local
*.log*

# Dist
node_modules
dist/

# IDE
.vscode/*
!.vscode/extensions.json
.idea

# generated archives
*.tar.gz

# external CUE dependencies
/*/cue.mod/pkg/
21 changes: 21 additions & 0 deletions logexplorer/.swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"target": "es2022",
"transform": {
"react": {
"runtime": "automatic",
"useBuiltins": true
}
}
},
"module": {
"type": "es6"
},
"sourceMaps": true,
"exclude": ["\\.(stories|test)\\."]
}
41 changes: 41 additions & 0 deletions logexplorer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Plugin Module: log-explorer

### How to install

This plugin requires react and react-dom 18

Install peer dependencies:

```bash
npm install react@18 react-dom@18
```

Install the plugin:

```bash
npm install @my-org/log-explorer
```

## Development

### Setup

Install dependencies:

```bash
npm install
```

### Get Started

Start the dev server:

```bash
npm run dev
```

Build the plugin for distribution:

```bash
npm run build
```
7 changes: 7 additions & 0 deletions logexplorer/cue.mod/module.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module: "github.com/perses-dev/log-explorer@v0"
language: {
version: "v0.12.0"
}
source: {
kind: "git"
}
26 changes: 26 additions & 0 deletions logexplorer/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright The Perses Authors
// Licensed under the Apache License, Version 2.0 (the \"License\");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an \"AS IS\" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import type { Config } from "@jest/types";
import shared from "../jest.shared";

const jestConfig: Config.InitialOptions = {
...shared,

setupFilesAfterEnv: [
...(shared.setupFilesAfterEnv ?? []),
"<rootDir>/src/setup-tests.ts",
],
};

export default jestConfig;
72 changes: 72 additions & 0 deletions logexplorer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"name": "@perses-dev/log-explorer",
"version": "0.1.0",
"homepage": "https://github.com/perses/plugins/blob/main/README.md",
"repository": {
"type": "git",
"url": "git+https://github.com/perses/plugins.git"
},
"bugs": {
"url": "https://github.com/perses/plugins/issues"
},
"scripts": {
"dev": "rsbuild dev",
"build": "npm run build-mf && concurrently \"npm:build:*\"",
"build-mf": "rsbuild build",
"build:cjs": "swc ./src -d dist/lib/cjs --strip-leading-paths --config-file .cjs.swcrc",
"build:esm": "swc ./src -d dist/lib --strip-leading-paths --config-file .swcrc",
"build:types": "tsc --project tsconfig.build.json",
"lint": "eslint src --ext .ts,.tsx",
"test": "cross-env LC_ALL=C TZ=UTC jest --passWithNoTests",
"type-check": "tsc --noEmit"
},
"main": "lib/cjs/index.js",
"module": "lib/index.js",
"types": "lib/index.d.ts",
"devDependencies": {
"@types/qs": "^6.9.18"
},
"peerDependencies": {
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@hookform/resolvers": "^3.2.0",
"@perses-dev/components": "^0.53.0-rc.2",
"@perses-dev/core": "^0.53.0-rc.0",
"@perses-dev/dashboards": "^0.53.0-rc.2",
"@perses-dev/explore": "^0.53.0-rc.2",
"@perses-dev/plugin-system": "^0.53.0-rc.2",
"@tanstack/react-query": "^4.39.1",
"date-fns": "^4.1.0",
"date-fns-tz": "^3.2.0",
"echarts": "5.5.0",
"immer": "^10.1.1",
"lodash": "^4.17.21",
"react": "^17.0.2 || ^18.0.0",
"react-dom": "^17.0.2 || ^18.0.0",
"react-hook-form": "^7.52.2",
"react-router-dom": "^5 || ^6 || ^7",
"use-resize-observer": "^9.0.0"
},
"files": [
"lib/**/*",
"__mf/**/*",
"mf-manifest.json",
"mf-stats.json"
],
"perses": {
"moduleName": "LogExplorer",
"moduleOrg": "perses.dev",
"schemasPath": "schemas",
"plugins": [
{
"kind": "Explore",
"spec": {
"display": {
"name": "Log Explorer"
},
"name": "LogExplorer"
}
}
]
}
}
46 changes: 46 additions & 0 deletions logexplorer/rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright The Perses Authors
// Licensed under the Apache License, Version 2.0 (the \"License\");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an \"AS IS\" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { pluginReact } from '@rsbuild/plugin-react';
import { createConfigForPlugin } from '../rsbuild.shared';

export default createConfigForPlugin({
name: 'LogExplorer',
rsbuildConfig: {
server: { port: 3009 },
plugins: [pluginReact()],
},
moduleFederation: {
exposes: {
'./LogExplorer': './src/explore/log-explorer',
},
shared: {
react: { requiredVersion: '18.2.0', singleton: true },
'react-dom': { requiredVersion: '18.2.0', singleton: true },
echarts: { singleton: true },
'date-fns': { singleton: true },
'date-fns-tz': { singleton: true },
lodash: { singleton: true },
'@perses-dev/components': { singleton: true },
'@perses-dev/plugin-system': { singleton: true },
'@perses-dev/explore': { singleton: true },
'@perses-dev/dashboards': { singleton: true },
'@emotion/react': { requiredVersion: '^11.11.3', singleton: true },
'@emotion/styled': { singleton: true },
'@hookform/resolvers': { singleton: true },
'@tanstack/react-query': { singleton: true },
'react-hook-form': { singleton: true },
'react-router-dom': { singleton: true },
},
},
});
18 changes: 18 additions & 0 deletions logexplorer/src/bootstrap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright The Perses Authors
// Licensed under the Apache License, Version 2.0 (the \"License\");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an \"AS IS\" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import React from 'react';
import ReactDOM from 'react-dom/client';

const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(<React.StrictMode></React.StrictMode>);
14 changes: 14 additions & 0 deletions logexplorer/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright The Perses Authors
// Licensed under the Apache License, Version 2.0 (the \"License\");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an \"AS IS\" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// <reference types="@rsbuild/core/types" />
14 changes: 14 additions & 0 deletions logexplorer/src/explore/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright The Perses Authors
// Licensed under the Apache License, Version 2.0 (the \"License\");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an \"AS IS\" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

export * from './log-explorer';
Loading
Loading