Compare commits

..

14 Commits

Author SHA1 Message Date
Ankit Nayan
a5fd338a9d release: v0.4.1 2021-09-28 20:32:09 +05:30
Ankit Nayan
8a781076e1 Revert "fix: frontend/package.json, frontend/yarn.lock & frontend/.snyk to reduce vulnerabilities (#310)" (#315)
This reverts commit e756cefa75.
2021-09-28 19:25:32 +05:30
Palash
18fc697b91 Fix dark theme mode (#314)
* theme address is fixed

* theme address is fixed
2021-09-28 19:03:19 +05:30
Palash
93b347d25e Fix(FE): dark mode (#301)
* fix: fav icon is fixed and bootstrap is removed

* fix: return type is updated for the global time reducer

* fix: theme.css is replaced with .min.css

* update: useThemeSwitcher is removed from the graph component and value is grabed from the reducer

* update: instrumentation page is updated

* update: react-css-theme-switcher package is removed

* update: darkMode is updated

* fix: Sider component is updated
2021-09-28 18:50:10 +05:30
Palash
ea5b40c7ea Feat(FE): Delete Query, Save Layout (#306)
* feat: Delete Query functionality is added

* feat: save layout is updated
2021-09-28 18:38:34 +05:30
Palash
cc91242e9a Fix(FE): Fix date dashboard (#311)
* chore: getFormatedDate function is added

* fix: date format in the all dashboard is updated to mm/dd/yyyy HH:MM
2021-09-28 18:32:02 +05:30
Snyk bot
e756cefa75 fix: frontend/package.json, frontend/yarn.lock & frontend/.snyk to reduce vulnerabilities (#310)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-ANSIREGEX-1583908
- https://snyk.io/vuln/SNYK-JS-AXIOS-1579269
- https://snyk.io/vuln/SNYK-JS-D3COLOR-1076592
- https://snyk.io/vuln/SNYK-JS-GLOBPARENT-1016905


The following vulnerabilities are fixed with a Snyk patch:
- https://snyk.io/vuln/npm:debug:20170905
2021-09-28 18:25:41 +05:30
Palash
da653681cf fix: color for the graph is updated (#305) 2021-09-28 18:20:38 +05:30
Palash
93b5a945a4 fix: Chart.js plugins are not register at each and every render (#303) 2021-09-28 18:20:14 +05:30
Palash
9ab1093d81 fix: createdBy is renamed to createdAt (#302) 2021-09-28 18:19:30 +05:30
Palash
b4754053aa fix: fav icon is fixed and bootstrap is removed (#299) 2021-09-28 18:18:30 +05:30
Ankit Nayan
8fef964485 feat: persisting database for dashboards 2021-09-28 18:14:12 +05:30
Ankit Nayan
004dda200c feat: signoz can now scale up in docker swarm (#309)
* feat: signoz can now scale up in docker swarm

* chore: adding empty folders for volume mount

* chore: using image 0.4.0

* chore: adding folder to persist signoz.db
2021-09-28 18:10:44 +05:30
Ankit Nayan
6a01ce88cb chore: exit application if clickhouse is not reachable (#308) 2021-09-27 23:20:27 +05:30
58 changed files with 1369 additions and 50088 deletions

View File

@@ -0,0 +1,517 @@
<?xml version="1.0"?>
<yandex>
<logger>
<level>trace</level>
<log>/var/log/clickhouse-server/clickhouse-server.log</log>
<errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
<size>1000M</size>
<count>10</count>
</logger>
<http_port>8123</http_port>
<tcp_port>9000</tcp_port>
<!-- For HTTPS and SSL over native protocol. -->
<!--
<https_port>8443</https_port>
<tcp_ssl_port>9440</tcp_ssl_port>
-->
<!-- Used with https_port and tcp_ssl_port. Full ssl options list: https://github.com/yandex/ClickHouse/blob/master/contrib/libpoco/NetSSL_OpenSSL/include/Poco/Net/SSLManager.h#L71 -->
<openSSL>
<server> <!-- Used for https server AND secure tcp port -->
<!-- openssl req -subj "/CN=localhost" -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout /etc/clickhouse-server/server.key -out /etc/clickhouse-server/server.crt -->
<certificateFile>/etc/clickhouse-server/server.crt</certificateFile>
<privateKeyFile>/etc/clickhouse-server/server.key</privateKeyFile>
<!-- openssl dhparam -out /etc/clickhouse-server/dhparam.pem 4096 -->
<dhParamsFile>/etc/clickhouse-server/dhparam.pem</dhParamsFile>
<verificationMode>none</verificationMode>
<loadDefaultCAFile>true</loadDefaultCAFile>
<cacheSessions>true</cacheSessions>
<disableProtocols>sslv2,sslv3</disableProtocols>
<preferServerCiphers>true</preferServerCiphers>
</server>
<client> <!-- Used for connecting to https dictionary source -->
<loadDefaultCAFile>true</loadDefaultCAFile>
<cacheSessions>true</cacheSessions>
<disableProtocols>sslv2,sslv3</disableProtocols>
<preferServerCiphers>true</preferServerCiphers>
<!-- Use for self-signed: <verificationMode>none</verificationMode> -->
<invalidCertificateHandler>
<!-- Use for self-signed: <name>AcceptCertificateHandler</name> -->
<name>RejectCertificateHandler</name>
</invalidCertificateHandler>
</client>
</openSSL>
<!-- Default root page on http[s] server. For example load UI from https://tabix.io/ when opening http://localhost:8123 -->
<!--
<http_server_default_response><![CDATA[<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>]]></http_server_default_response>
-->
<!-- Port for communication between replicas. Used for data exchange. -->
<interserver_http_port>9009</interserver_http_port>
<!-- Hostname that is used by other replicas to request this server.
If not specified, than it is determined analoguous to 'hostname -f' command.
This setting could be used to switch replication to another network interface.
-->
<!--
<interserver_http_host>example.yandex.ru</interserver_http_host>
-->
<!-- Listen specified host. use :: (wildcard IPv6 address), if you want to accept connections both with IPv4 and IPv6 from everywhere. -->
<listen_host>::</listen_host>
<!-- Same for hosts with disabled ipv6: -->
<!-- <listen_host>0.0.0.0</listen_host> -->
<!-- Default values - try listen localhost on ipv4 and ipv6: -->
<!-- <listen_host>0.0.0.0</listen_host> -->
<max_connections>4096</max_connections>
<keep_alive_timeout>3</keep_alive_timeout>
<!-- Maximum number of concurrent queries. -->
<max_concurrent_queries>100</max_concurrent_queries>
<!-- Set limit on number of open files (default: maximum). This setting makes sense on Mac OS X because getrlimit() fails to retrieve
correct maximum value. -->
<!-- <max_open_files>262144</max_open_files> -->
<!-- Size of cache of uncompressed blocks of data, used in tables of MergeTree family.
In bytes. Cache is single for server. Memory is allocated only on demand.
Cache is used when 'use_uncompressed_cache' user setting turned on (off by default).
Uncompressed cache is advantageous only for very short queries and in rare cases.
-->
<uncompressed_cache_size>8589934592</uncompressed_cache_size>
<!-- Approximate size of mark cache, used in tables of MergeTree family.
In bytes. Cache is single for server. Memory is allocated only on demand.
You should not lower this value.
-->
<mark_cache_size>5368709120</mark_cache_size>
<!-- Path to data directory, with trailing slash. -->
<path>/var/lib/clickhouse/</path>
<!-- Path to temporary data for processing hard queries. -->
<tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
<!-- Path to configuration file with users, access rights, profiles of settings, quotas. -->
<users_config>users.xml</users_config>
<!-- Default profile of settings.. -->
<default_profile>default</default_profile>
<!-- Default database. -->
<default_database>default</default_database>
<!-- Server time zone could be set here.
Time zone is used when converting between String and DateTime types,
when printing DateTime in text formats and parsing DateTime from text,
it is used in date and time related functions, if specific time zone was not passed as an argument.
Time zone is specified as identifier from IANA time zone database, like UTC or Africa/Abidjan.
If not specified, system time zone at server startup is used.
Please note, that server could display time zone alias instead of specified name.
Example: W-SU is an alias for Europe/Moscow and Zulu is an alias for UTC.
-->
<!-- <timezone>Europe/Moscow</timezone> -->
<!-- You can specify umask here (see "man umask"). Server will apply it on startup.
Number is always parsed as octal. Default umask is 027 (other users cannot read logs, data files, etc; group can only read).
-->
<!-- <umask>022</umask> -->
<!-- Configuration of clusters that could be used in Distributed tables.
https://clickhouse.yandex/reference_en.html#Distributed
-->
<remote_servers incl="clickhouse_remote_servers" >
<!-- Test only shard config for testing distributed storage -->
<test_shard_localhost>
<shard>
<replica>
<host>localhost</host>
<port>9000</port>
</replica>
</shard>
</test_shard_localhost>
</remote_servers>
<!-- If element has 'incl' attribute, then for it's value will be used corresponding substitution from another file.
By default, path to file with substitutions is /etc/metrika.xml. It could be changed in config in 'include_from' element.
Values for substitutions are specified in /yandex/name_of_substitution elements in that file.
-->
<!-- ZooKeeper is used to store metadata about replicas, when using Replicated tables.
Optional. If you don't use replicated tables, you could omit that.
See https://clickhouse.yandex/reference_en.html#Data%20replication
-->
<zookeeper incl="zookeeper-servers" optional="true" />
<!-- Substitutions for parameters of replicated tables.
Optional. If you don't use replicated tables, you could omit that.
See https://clickhouse.yandex/reference_en.html#Creating%20replicated%20tables
-->
<macros incl="macros" optional="true" />
<!-- Reloading interval for embedded dictionaries, in seconds. Default: 3600. -->
<builtin_dictionaries_reload_interval>3600</builtin_dictionaries_reload_interval>
<!-- Maximum session timeout, in seconds. Default: 3600. -->
<max_session_timeout>3600</max_session_timeout>
<!-- Default session timeout, in seconds. Default: 60. -->
<default_session_timeout>60</default_session_timeout>
<!-- Sending data to Graphite for monitoring. Several sections can be defined. -->
<!--
interval - send every X second
root_path - prefix for keys
hostname_in_path - append hostname to root_path (default = true)
metrics - send data from table system.metrics
events - send data from table system.events
asynchronous_metrics - send data from table system.asynchronous_metrics
-->
<!--
<graphite>
<host>localhost</host>
<port>42000</port>
<timeout>0.1</timeout>
<interval>60</interval>
<root_path>one_min</root_path>
<hostname_in_path>true<hostname_in_path>
<metrics>true</metrics>
<events>true</events>
<asynchronous_metrics>true</asynchronous_metrics>
</graphite>
<graphite>
<host>localhost</host>
<port>42000</port>
<timeout>0.1</timeout>
<interval>1</interval>
<root_path>one_sec</root_path>
<metrics>true</metrics>
<events>true</events>
<asynchronous_metrics>false</asynchronous_metrics>
</graphite>
-->
<!-- Query log. Used only for queries with setting log_queries = 1. -->
<query_log>
<!-- What table to insert data. If table is not exist, it will be created.
When query log structure is changed after system update,
then old table will be renamed and new table will be created automatically.
-->
<database>system</database>
<table>query_log</table>
<!-- Interval of flushing data. -->
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</query_log>
<!-- Uncomment if use part_log
<part_log>
<database>system</database>
<table>part_log</table>
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</part_log>
-->
<!-- Parameters for embedded dictionaries, used in Yandex.Metrica.
See https://clickhouse.yandex/reference_en.html#Internal%20dictionaries
-->
<!-- Path to file with region hierarchy. -->
<!-- <path_to_regions_hierarchy_file>/opt/geo/regions_hierarchy.txt</path_to_regions_hierarchy_file> -->
<!-- Path to directory with files containing names of regions -->
<!-- <path_to_regions_names_files>/opt/geo/</path_to_regions_names_files> -->
<!-- Configuration of external dictionaries. See:
https://clickhouse.yandex/reference_en.html#External%20Dictionaries
-->
<dictionaries_config>*_dictionary.xml</dictionaries_config>
<!-- Uncomment if you want data to be compressed 30-100% better.
Don't do that if you just started using ClickHouse.
-->
<compression incl="clickhouse_compression">
<!--
<!- - Set of variants. Checked in order. Last matching case wins. If nothing matches, lz4 will be used. - ->
<case>
<!- - Conditions. All must be satisfied. Some conditions may be omitted. - ->
<min_part_size>10000000000</min_part_size> <!- - Min part size in bytes. - ->
<min_part_size_ratio>0.01</min_part_size_ratio> <!- - Min size of part relative to whole table size. - ->
<!- - What compression method to use. - ->
<method>zstd</method>
</case>
-->
</compression>
<!-- Allow to execute distributed DDL queries (CREATE, DROP, ALTER, RENAME) on cluster.
Works only if ZooKeeper is enabled. Comment it if such functionality isn't required. -->
<distributed_ddl>
<!-- Path in ZooKeeper to queue with DDL queries -->
<path>/clickhouse/task_queue/ddl</path>
</distributed_ddl>
<!-- Settings to fine tune MergeTree tables. See documentation in source code, in MergeTreeSettings.h -->
<!--
<merge_tree>
<max_suspicious_broken_parts>5</max_suspicious_broken_parts>
</merge_tree>
-->
<!-- Protection from accidental DROP.
If size of a MergeTree table is greater than max_table_size_to_drop (in bytes) than table could not be dropped with any DROP query.
If you want do delete one table and don't want to restart clickhouse-server, you could create special file <clickhouse-path>/flags/force_drop_table and make DROP once.
By default max_table_size_to_drop is 50GB, max_table_size_to_drop=0 allows to DROP any tables.
Uncomment to disable protection.
-->
<!-- <max_table_size_to_drop>0</max_table_size_to_drop> -->
<!-- Example of parameters for GraphiteMergeTree table engine -->
<graphite_rollup>
<!-- carbon -->
<pattern>
<regexp>^carbon\.</regexp>
<function>any</function>
<retention>
<age>0</age>
<precision>60</precision>
</retention>
<retention>
<age>7776000</age>
<precision>3600</precision>
</retention>
<retention>
<age>10368000</age>
<precision>21600</precision>
</retention>
<retention>
<age>34560000</age>
<precision>43200</precision>
</retention>
<retention>
<age>63072000</age>
<precision>86400</precision>
</retention>
<retention>
<age>94608000</age>
<precision>604800</precision>
</retention>
</pattern>
<!-- collectd -->
<pattern>
<regexp>^collectd\.</regexp>
<function>any</function>
<retention>
<age>0</age>
<precision>10</precision>
</retention>
<retention>
<age>43200</age>
<precision>60</precision>
</retention>
<retention>
<age>864000</age>
<precision>900</precision>
</retention>
<retention>
<age>1728000</age>
<precision>1800</precision>
</retention>
<retention>
<age>3456000</age>
<precision>3600</precision>
</retention>
<retention>
<age>10368000</age>
<precision>21600</precision>
</retention>
<retention>
<age>34560000</age>
<precision>43200</precision>
</retention>
<retention>
<age>63072000</age>
<precision>86400</precision>
</retention>
<retention>
<age>94608000</age>
<precision>604800</precision>
</retention>
</pattern>
<!-- high -->
<pattern>
<regexp>^high\.</regexp>
<function>any</function>
<retention>
<age>0</age>
<precision>10</precision>
</retention>
<retention>
<age>172800</age>
<precision>60</precision>
</retention>
<retention>
<age>864000</age>
<precision>900</precision>
</retention>
<retention>
<age>1728000</age>
<precision>1800</precision>
</retention>
<retention>
<age>3456000</age>
<precision>3600</precision>
</retention>
<retention>
<age>10368000</age>
<precision>21600</precision>
</retention>
<retention>
<age>34560000</age>
<precision>43200</precision>
</retention>
<retention>
<age>63072000</age>
<precision>86400</precision>
</retention>
<retention>
<age>94608000</age>
<precision>604800</precision>
</retention>
</pattern>
<!-- medium -->
<pattern>
<regexp>^medium\.</regexp>
<function>any</function>
<retention>
<age>0</age>
<precision>60</precision>
</retention>
<retention>
<age>864000</age>
<precision>900</precision>
</retention>
<retention>
<age>1728000</age>
<precision>1800</precision>
</retention>
<retention>
<age>3456000</age>
<precision>3600</precision>
</retention>
<retention>
<age>10368000</age>
<precision>21600</precision>
</retention>
<retention>
<age>34560000</age>
<precision>43200</precision>
</retention>
<retention>
<age>63072000</age>
<precision>86400</precision>
</retention>
<retention>
<age>94608000</age>
<precision>604800</precision>
</retention>
</pattern>
<!-- low -->
<pattern>
<regexp>^low\.</regexp>
<function>any</function>
<retention>
<age>0</age>
<precision>600</precision>
</retention>
<retention>
<age>15552000</age>
<precision>1800</precision>
</retention>
<retention>
<age>31536000</age>
<precision>3600</precision>
</retention>
<retention>
<age>63072000</age>
<precision>21600</precision>
</retention>
<retention>
<age>126144000</age>
<precision>43200</precision>
</retention>
<retention>
<age>252288000</age>
<precision>86400</precision>
</retention>
<retention>
<age>315360000</age>
<precision>604800</precision>
</retention>
</pattern>
<!-- default -->
<default>
<function>any</function>
<retention>
<age>0</age>
<precision>60</precision>
</retention>
<retention>
<age>864000</age>
<precision>900</precision>
</retention>
<retention>
<age>1728000</age>
<precision>1800</precision>
</retention>
<retention>
<age>3456000</age>
<precision>3600</precision>
</retention>
<retention>
<age>10368000</age>
<precision>21600</precision>
</retention>
<retention>
<age>34560000</age>
<precision>43200</precision>
</retention>
<retention>
<age>63072000</age>
<precision>86400</precision>
</retention>
<retention>
<age>94608000</age>
<precision>604800</precision>
</retention>
</default>
</graphite_rollup>
<!-- Directory in <clickhouse-path> containing schema files for various input formats.
The directory will be created if it doesn't exist.
-->
<format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
</yandex>

View File

@@ -0,0 +1,113 @@
version: "3"
services:
clickhouse:
image: yandex/clickhouse-server
expose:
- 8123
- 9000
ports:
- 9001:9000
- 8123:8123
volumes:
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
- ./docker-entrypoint-initdb.d/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
- ./data/clickhouse/:/var/lib/clickhouse/
healthcheck:
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
interval: 30s
timeout: 5s
retries: 3
query-service:
image: signoz/query-service:0.4.1
container_name: query-service
restart: always
command: ["-config=/root/config/prometheus.yml"]
ports:
- "8080:8080"
volumes:
- ./prometheus.yml:/root/config/prometheus.yml
- ../dashboards:/root/config/dashboards
- ./data/signoz/:/var/lib/signoz/
environment:
- ClickHouseUrl=tcp://clickhouse:9000
- STORAGE=clickhouse
- POSTHOG_API_KEY=H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w
- GODEBUG=netdns=go
depends_on:
- clickhouse
frontend:
image: signoz/frontend:0.4.1
container_name: frontend
depends_on:
- query-service
links:
- "query-service"
ports:
- "3000:3000"
volumes:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
image: signoz/otelcontribcol:0.4.0
command: ["--config=/etc/otel-collector-config.yaml", "--mem-ballast-size-mib=2000"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "1777:1777" # pprof extension
- "8887:8888" # Prometheus metrics exposed by the agent
- "14268:14268" # Jaeger receiver
- "55678" # OpenCensus receiver
- "55680:55680" # OTLP HTTP/2.0 legacy port
- "55681:55681" # OTLP HTTP/1.0 receiver
- "4317:4317" # OTLP GRPC receiver
- "55679:55679" # zpages extension
- "13133" # health_check
deploy:
mode: replicated
replicas: 3
depends_on:
- clickhouse
otel-collector-hostmetrics:
image: signoz/otelcontribcol:0.4.0
command: ["--config=/etc/otel-collector-config-hostmetrics.yaml", "--mem-ballast-size-mib=683"]
volumes:
- ./otel-collector-config-hostmetrics.yaml:/etc/otel-collector-config-hostmetrics.yaml
depends_on:
- clickhouse
hotrod:
image: jaegertracing/example-hotrod:latest
container_name: hotrod
ports:
- "9000:8080"
command: ["all"]
environment:
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
load-hotrod:
image: "grubykarol/locust:1.2.3-python3.9-alpine3.12"
container_name: load-hotrod
hostname: load-hotrod
ports:
- "8089:8089"
environment:
ATTACKED_HOST: http://hotrod:8080
LOCUST_MODE: standalone
NO_PROXY: standalone
TASK_DELAY_FROM: 5
TASK_DELAY_TO: 30
QUIET_MODE: "${QUIET_MODE:-false}"
LOCUST_OPTS: "--headless -u 10 -r 1"
volumes:
- ../common/locust-scripts:/locust

View File

@@ -0,0 +1,27 @@
CREATE TABLE IF NOT EXISTS signoz_index (
timestamp DateTime64(9) CODEC(Delta, ZSTD(1)),
traceID String CODEC(ZSTD(1)),
spanID String CODEC(ZSTD(1)),
parentSpanID String CODEC(ZSTD(1)),
serviceName LowCardinality(String) CODEC(ZSTD(1)),
name LowCardinality(String) CODEC(ZSTD(1)),
kind Int32 CODEC(ZSTD(1)),
durationNano UInt64 CODEC(ZSTD(1)),
tags Array(String) CODEC(ZSTD(1)),
tagsKeys Array(String) CODEC(ZSTD(1)),
tagsValues Array(String) CODEC(ZSTD(1)),
statusCode Int64 CODEC(ZSTD(1)),
references String CODEC(ZSTD(1)),
externalHttpMethod Nullable(String) CODEC(ZSTD(1)),
externalHttpUrl Nullable(String) CODEC(ZSTD(1)),
component Nullable(String) CODEC(ZSTD(1)),
dbSystem Nullable(String) CODEC(ZSTD(1)),
dbName Nullable(String) CODEC(ZSTD(1)),
dbOperation Nullable(String) CODEC(ZSTD(1)),
peerService Nullable(String) CODEC(ZSTD(1)),
INDEX idx_tagsKeys tagsKeys TYPE bloom_filter(0.01) GRANULARITY 64,
INDEX idx_tagsValues tagsValues TYPE bloom_filter(0.01) GRANULARITY 64,
INDEX idx_duration durationNano TYPE minmax GRANULARITY 1
) ENGINE MergeTree()
PARTITION BY toDate(timestamp)
ORDER BY (serviceName, -toUnixTimestamp(timestamp))

View File

@@ -0,0 +1,72 @@
receivers:
otlp:
protocols:
grpc:
http:
jaeger:
protocols:
grpc:
thrift_http:
hostmetrics:
collection_interval: 60s
scrapers:
cpu:
load:
memory:
disk:
filesystem:
network:
# Data sources: metrics
prometheus:
config:
scrape_configs:
- job_name: "otel-collector"
dns_sd_configs:
- names:
- 'tasks.signoz_otel-collector'
type: 'A'
port: 8888
- job_name: "otel-collector-hostmetrics"
scrape_interval: 10s
static_configs:
- targets: ["otel-collector-hostmetrics:8888"]
processors:
batch:
send_batch_size: 1000
timeout: 10s
memory_limiter:
# Same as --mem-ballast-size-mib CLI argument
ballast_size_mib: 683
# 80% of maximum memory up to 2G
limit_mib: 1500
# 25% of limit up to 2G
spike_limit_mib: 512
check_interval: 5s
# queued_retry:
# num_workers: 4
# queue_size: 100
# retry_on_failure: true
extensions:
health_check: {}
zpages: {}
exporters:
clickhouse:
datasource: tcp://clickhouse:9000
clickhousemetricswrite:
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
resource_to_telemetry_conversion:
enabled: true
service:
extensions: [health_check, zpages]
pipelines:
traces:
receivers: [jaeger, otlp]
processors: [batch]
exporters: [clickhouse]
metrics:
receivers: [otlp, prometheus, hostmetrics]
processors: [batch]
exporters: [clickhousemetricswrite]

View File

@@ -0,0 +1,47 @@
receivers:
otlp:
protocols:
grpc:
http:
jaeger:
protocols:
grpc:
thrift_http:
processors:
batch:
send_batch_size: 1000
timeout: 10s
memory_limiter:
# Same as --mem-ballast-size-mib CLI argument
ballast_size_mib: 683
# 80% of maximum memory up to 2G
limit_mib: 1500
# 25% of limit up to 2G
spike_limit_mib: 512
check_interval: 5s
# queued_retry:
# num_workers: 4
# queue_size: 100
# retry_on_failure: true
extensions:
health_check: {}
zpages: {}
exporters:
clickhouse:
datasource: tcp://clickhouse:9000
clickhousemetricswrite:
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
resource_to_telemetry_conversion:
enabled: true
service:
extensions: [health_check, zpages]
pipelines:
traces:
receivers: [jaeger, otlp]
processors: [batch]
exporters: [clickhouse]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [clickhousemetricswrite]

View File

@@ -0,0 +1,25 @@
# my global config
global:
scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
remote_read:
- url: tcp://clickhouse:9000/?database=signoz_metrics

View File

@@ -0,0 +1,16 @@
from locust import HttpUser, task, between
class UserTasks(HttpUser):
wait_time = between(5, 15)
@task
def rachel(self):
self.client.get("/dispatch?customer=123&nonse=0.6308392664170006")
@task
def trom(self):
self.client.get("/dispatch?customer=392&nonse=0.015296363321630757")
@task
def japanese(self):
self.client.get("/dispatch?customer=731&nonse=0.8022286220408668")
@task
def coffee(self):
self.client.get("/dispatch?customer=567&nonse=0.0022220379420636593")

View File

@@ -0,0 +1,30 @@
server {
listen 3000;
server_name _;
gzip on;
gzip_static on;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://query-service:8080/api;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

View File

View File

@@ -22,7 +22,7 @@ services:
retries: 3
query-service:
image: signoz/query-service:0.4.0
image: signoz/query-service:0.4.1
container_name: query-service
command: ["-config=/root/config/prometheus.yml"]
ports:
@@ -30,6 +30,7 @@ services:
volumes:
- ./prometheus.yml:/root/config/prometheus.yml
- ../dashboards:/root/config/dashboards
- ./data/signoz/:/var/lib/signoz/
environment:
- ClickHouseUrl=tcp://clickhouse:9000
@@ -42,7 +43,7 @@ services:
condition: service_healthy
frontend:
image: signoz/frontend:0.4.0
image: signoz/frontend:0.4.1
container_name: frontend
depends_on:

View File

@@ -163,7 +163,7 @@ services:
query-service:
image: signoz.docker.scarf.sh/signoz/query-service:0.4.0
image: signoz.docker.scarf.sh/signoz/query-service:0.4.1
container_name: query-service
depends_on:
@@ -172,18 +172,20 @@ services:
- "8080:8080"
volumes:
- ../dashboards:/root/config/dashboards
- ./data/signoz/:/var/lib/signoz/
environment:
- DruidClientUrl=http://router:8888
- DruidDatasource=flattened_spans
- STORAGE=druid
- POSTHOG_API_KEY=H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w
- GODEBUG=netdns=go
depends_on:
router:
condition: service_healthy
frontend:
image: signoz/frontend:0.4.0
image: signoz/frontend:0.4.1
container_name: frontend
depends_on:

View File

@@ -158,7 +158,7 @@ services:
query-service:
image: signoz.docker.scarf.sh/signoz/query-service:0.4.0
image: signoz.docker.scarf.sh/signoz/query-service:0.4.1
container_name: query-service
depends_on:
@@ -168,18 +168,20 @@ services:
volumes:
- ../dashboards:/root/config/dashboards
- ./data/signoz/:/var/lib/signoz/
environment:
- DruidClientUrl=http://router:8888
- DruidDatasource=flattened_spans
- STORAGE=druid
- POSTHOG_API_KEY=H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w
- GODEBUG=netdns=go
depends_on:
router:
condition: service_healthy
frontend:
image: signoz/frontend:0.4.0
image: signoz/frontend:0.4.1
container_name: frontend
depends_on:

10
frontend/public/css/antd.dark.min.css vendored Normal file

File diff suppressed because one or more lines are too long

10
frontend/public/css/antd.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -1,5 +1,3 @@
@import '~antd/dist/antd.dark.css';
.ant-space-item {
margin-right: 0 !important;
}

View File

@@ -24,10 +24,29 @@ import chartjsAdapter from 'chartjs-adapter-date-fns';
// import { colors } from 'lib/getRandomColor';
// import stringToHTML from 'lib/stringToHTML';
import React, { useCallback, useEffect, useRef } from 'react';
import { useThemeSwitcher } from 'react-css-theme-switcher';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
// import Legends from './Legend';
// import { LegendsContainer } from './styles';
Chart.register(
LineElement,
PointElement,
LineController,
CategoryScale,
LinearScale,
TimeScale,
TimeSeriesScale,
Decimation,
Filler,
Legend,
Title,
Tooltip,
SubTitle,
BarController,
BarElement,
);
const Graph = ({
data,
@@ -38,8 +57,9 @@ const Graph = ({
xAxisType,
onClickHandler,
}: GraphProps): JSX.Element => {
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const chartRef = useRef<HTMLCanvasElement>(null);
const { currentTheme } = useThemeSwitcher();
const currentTheme = isDarkMode ? 'dark' : 'light';
// const [tooltipVisible, setTooltipVisible] = useState<boolean>(false);
const lineChartRef = useRef<Chart>();
@@ -62,24 +82,6 @@ const Graph = ({
}
if (chartRef.current !== null) {
Chart.register(
LineElement,
PointElement,
LineController,
CategoryScale,
LinearScale,
TimeScale,
TimeSeriesScale,
Decimation,
Filler,
Legend,
Title,
Tooltip,
SubTitle,
BarController,
BarElement,
);
const options: ChartOptions = {
responsive: true,
maintainAspectRatio: false,

View File

@@ -1,28 +1,45 @@
import { Layout, Menu, Switch as ToggleButton, Typography } from 'antd';
import { Menu, Switch as ToggleButton, Typography } from 'antd';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import React, { useCallback, useState } from 'react';
import { useThemeSwitcher } from 'react-css-theme-switcher';
import { connect, useSelector } from 'react-redux';
import { NavLink } from 'react-router-dom';
import { useLocation } from 'react-router-dom';
import { Logo, ThemeSwitcherWrapper } from './styles';
const { Sider } = Layout;
import history from 'lib/history';
import { bindActionCreators } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { ToggleDarkMode } from 'store/actions';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import AppReducer from 'types/reducer/app';
import menus from './menuItems';
import { Logo, Sider, ThemeSwitcherWrapper } from './styles';
const SideNav = (): JSX.Element => {
const { switcher, currentTheme, themes } = useThemeSwitcher();
const SideNav = ({ toggleDarkMode }: Props): JSX.Element => {
const [collapsed, setCollapsed] = useState<boolean>(false);
const { pathname } = useLocation();
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const toggleTheme = useCallback(
(isChecked: boolean) => {
switcher({ theme: isChecked ? themes.dark : themes.light });
},
[switcher, themes],
);
const toggleTheme = useCallback(() => {
const preMode: mode = isDarkMode ? 'lightMode' : 'darkMode';
const postMode: mode = isDarkMode ? 'darkMode' : 'lightMode';
const id: mode = preMode;
const head = document.head;
const link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = !isDarkMode ? '/css/antd.dark.min.css' : '/css/antd.min.css';
link.media = 'all';
link.id = id;
head.appendChild(link);
link.onload = (): void => {
toggleDarkMode();
const prevNode = document.getElementById(postMode);
prevNode?.remove();
};
}, [toggleDarkMode, isDarkMode]);
const onCollapse = useCallback(() => {
setCollapsed((collapsed) => !collapsed);
@@ -33,12 +50,9 @@ const SideNav = (): JSX.Element => {
}, []);
return (
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={160}>
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
<ThemeSwitcherWrapper>
<ToggleButton
checked={currentTheme === themes.dark}
onChange={toggleTheme}
/>
<ToggleButton checked={isDarkMode} onChange={toggleTheme} />
</ThemeSwitcherWrapper>
<NavLink to="/">
<Logo src={'/signoz.svg'} alt="SigNoz" collapsed={collapsed} />
@@ -62,4 +76,18 @@ const SideNav = (): JSX.Element => {
);
};
export default SideNav;
type mode = 'darkMode' | 'lightMode';
interface DispatchProps {
toggleDarkMode: () => void;
}
const mapDispatchToProps = (
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
): DispatchProps => ({
toggleDarkMode: bindActionCreators(ToggleDarkMode, dispatch),
});
type Props = DispatchProps;
export default connect(null, mapDispatchToProps)(SideNav);

View File

@@ -1,4 +1,6 @@
import { Layout } from 'antd';
import styled from 'styled-components';
const { Sider: SiderComponent } = Layout;
export const ThemeSwitcherWrapper = styled.div`
display: flex;
@@ -16,3 +18,9 @@ export const Logo = styled.img<LogoProps>`
interface LogoProps {
collapsed: boolean;
}
export const Sider = styled(SiderComponent)`
.ant-typography {
color: white;
}
`;

View File

@@ -1,3 +1,6 @@
/* eslint-disable react/display-name */
import { SaveFilled } from '@ant-design/icons';
import updateDashboardApi from 'api/dashboard/update';
import Spinner from 'components/Spinner';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
@@ -10,7 +13,13 @@ import { v4 } from 'uuid';
import AddWidget from './AddWidget';
import Graph from './Graph';
import { Card, CardContainer, ReactGridLayout } from './styles';
import {
Button,
ButtonContainer,
Card,
CardContainer,
ReactGridLayout,
} from './styles';
const GridGraph = (): JSX.Element => {
const { push } = useHistory();
@@ -19,6 +28,12 @@ const GridGraph = (): JSX.Element => {
const { dashboards, loading } = useSelector<AppState, DashboardReducer>(
(state) => state.dashboards,
);
const [saveLayoutState, setSaveLayoutState] = useState<State>({
loading: false,
error: false,
errorMessage: '',
payload: [],
});
const [selectedDashboard] = dashboards;
const { data } = selectedDashboard;
@@ -31,33 +46,41 @@ const GridGraph = (): JSX.Element => {
const isMounted = useRef(true);
const isDeleted = useRef(false);
const getPreLayouts: () => LayoutProps[] = useCallback(() => {
if (widgets === undefined) {
return [];
}
if (data.layout === undefined) {
return widgets.map((e, index) => {
return {
h: 2,
w: 6,
y: Infinity,
i: (index + 1).toString(),
x: (index % 2) * 6,
Component: (): JSX.Element => (
<Graph isDeleted={isDeleted} widget={widgets[index]} />
),
};
});
} else {
return data.layout.map((e, index) => ({
...e,
y: 0,
Component: (): JSX.Element => (
<Graph isDeleted={isDeleted} widget={widgets[index]} />
),
}));
}
}, [widgets, data.layout]);
useEffect(() => {
if (
loading === false &&
(isMounted.current === true || isDeleted.current === true)
) {
const getPreLayouts = (): LayoutProps[] => {
if (widgets === undefined) {
return [];
}
return widgets.map((e, index) => {
return {
h: 2,
w: 6,
y: Infinity,
i: (index + 1).toString(),
x: (index % 2) * 6,
// eslint-disable-next-line react/display-name
Component: (): JSX.Element => (
<Graph isDeleted={isDeleted} widget={widgets[index]} />
),
};
});
};
const preLayouts = getPreLayouts();
setLayout(() => [
...preLayouts,
{
@@ -67,6 +90,10 @@ const GridGraph = (): JSX.Element => {
w: 6,
h: 2,
Component: AddWidgetWrapper,
maxW: 6,
isDraggable: false,
isResizable: false,
isBounded: true,
},
]);
}
@@ -74,7 +101,7 @@ const GridGraph = (): JSX.Element => {
return (): void => {
isMounted.current = false;
};
}, [widgets, layouts.length, AddWidgetWrapper, loading]);
}, [widgets, layouts.length, AddWidgetWrapper, loading, getPreLayouts]);
const onDropHandler = useCallback(
(allLayouts: Layout[], currectLayout: Layout, event: DragEvent) => {
@@ -88,38 +115,95 @@ const GridGraph = (): JSX.Element => {
[pathname, push],
);
const onLayoutSaveHanlder = async (): Promise<void> => {
setSaveLayoutState((state) => ({
...state,
error: false,
errorMessage: '',
loading: true,
}));
const response = await updateDashboardApi({
title: data.title,
uuid: selectedDashboard.uuid,
description: data.description,
name: data.name,
tags: data.tags,
widgets: data.widgets,
layout: saveLayoutState.payload.filter((e) => e.maxW === undefined),
});
if (response.statusCode === 200) {
setSaveLayoutState((state) => ({
...state,
error: false,
errorMessage: '',
loading: false,
}));
} else {
setSaveLayoutState((state) => ({
...state,
error: true,
errorMessage: response.error || 'Something went wrong',
loading: false,
}));
}
};
const onLayoutChangeHandler = (layout: Layout[]): void => {
setSaveLayoutState({
loading: false,
error: false,
errorMessage: '',
payload: layout,
});
};
if (layouts.length === 0) {
return <Spinner height="40vh" size="large" tip="Loading..." />;
}
return (
<ReactGridLayout
isResizable
isDraggable
cols={12}
rowHeight={100}
autoSize
width={100}
isDroppable
useCSSTransforms
onDrop={onDropHandler}
>
{layouts.map(({ Component, ...rest }, index) => {
const widget = (widgets || [])[index] || {};
<>
<ButtonContainer>
<Button
loading={saveLayoutState.loading}
onClick={onLayoutSaveHanlder}
icon={<SaveFilled />}
danger={saveLayoutState.error}
>
Save Layout
</Button>
</ButtonContainer>
const type = widget.panelTypes;
<ReactGridLayout
isResizable
isDraggable
cols={12}
rowHeight={100}
autoSize
width={100}
isDroppable
useCSSTransforms
onDrop={onDropHandler}
onLayoutChange={onLayoutChangeHandler}
>
{layouts.map(({ Component, ...rest }, index) => {
const widget = (widgets || [])[index] || {};
const isQueryType = type === 'VALUE';
const type = widget.panelTypes;
return (
<CardContainer key={rest.i} data-grid={rest}>
<Card isQueryType={isQueryType}>
<Component />
</Card>
</CardContainer>
);
})}
</ReactGridLayout>
const isQueryType = type === 'VALUE';
return (
<CardContainer key={rest.i} data-grid={rest}>
<Card isQueryType={isQueryType}>
<Component />
</Card>
</CardContainer>
);
})}
</ReactGridLayout>
</>
);
};
@@ -127,4 +211,11 @@ interface LayoutProps extends Layout {
Component: () => JSX.Element;
}
interface State {
loading: boolean;
error: boolean;
payload: Layout[];
errorMessage: string;
}
export default memo(GridGraph);

View File

@@ -1,4 +1,4 @@
import { Card as CardComponent } from 'antd';
import { Button as ButtonComponent, Card as CardComponent } from 'antd';
import RGL, { WidthProvider } from 'react-grid-layout';
import styled from 'styled-components';
@@ -40,3 +40,17 @@ export const ReactGridLayout = styled(ReactGridLayoutComponent)`
margin-top: 1rem;
position: relative;
`;
export const ButtonContainer = styled.div`
display: flex;
justify-content: end;
margin-top: 1rem;
`;
export const Button = styled(ButtonComponent)`
&&& {
display: flex;
justify-content: center;
align-items: center;
}
`;

View File

@@ -1,5 +1,6 @@
import { Typography } from 'antd';
import convertDateToAmAndPm from 'lib/convertDateToAmAndPm';
import getFormattedDate from 'lib/getFormatedDate';
import React from 'react';
import { Data } from '..';
@@ -7,11 +8,11 @@ import { Data } from '..';
const Created = (createdBy: Data['createdBy']): JSX.Element => {
const time = new Date(createdBy);
return (
<Typography>{`${time.toLocaleDateString()} ${convertDateToAmAndPm(
time,
)}`}</Typography>
);
const date = getFormattedDate(time);
const timeString = `${date} ${convertDateToAmAndPm(time)}`;
return <Typography>{`${timeString}`}</Typography>;
};
export default Created;

View File

@@ -1,4 +1,6 @@
import { Typography } from 'antd';
import convertDateToAmAndPm from 'lib/convertDateToAmAndPm';
import getFormattedDate from 'lib/getFormatedDate';
import React from 'react';
import { Data } from '..';
@@ -6,9 +8,13 @@ import { Data } from '..';
const DateComponent = (
lastUpdatedTime: Data['lastUpdatedTime'],
): JSX.Element => {
const date = new Date(lastUpdatedTime).toDateString();
const time = new Date(lastUpdatedTime);
return <Typography>{date}</Typography>;
const date = getFormattedDate(time);
const timeString = `${date} ${convertDateToAmAndPm(time)}`;
return <Typography>{timeString}</Typography>;
};
export default DateComponent;

View File

@@ -47,15 +47,24 @@ const ListOfAllDashboard = (): JSX.Element => {
render: Tags,
},
{
title: 'Created By',
title: 'Created At',
dataIndex: 'createdBy',
sorter: (a: Data, b: Data): number => {
const prev = new Date(a.createdBy).getTime();
const next = new Date(b.createdBy).getTime();
return prev - next;
},
render: Createdby,
},
{
title: 'Last Updated Time',
dataIndex: 'lastUpdatedTime',
sorter: (a: Data, b: Data): number => {
return parseInt(a.lastUpdatedTime, 10) - parseInt(b.lastUpdatedTime, 10);
const prev = new Date(a.lastUpdatedTime).getTime();
const next = new Date(b.lastUpdatedTime).getTime();
return prev - next;
},
render: DateComponent,
},

View File

@@ -1,6 +1,6 @@
import GridGraphLayout from 'container/GridGraphLayout';
import ComponentsSlider from 'container/NewDashboard/ComponentsSlider';
import React, { useCallback, useState } from 'react';
import React from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import DashboardReducer from 'types/reducer/dashboards';

View File

@@ -5,10 +5,10 @@ import GridGraphs from './GridGraphs';
const NewDashboard = (): JSX.Element => {
return (
<div>
<>
<Description />
<GridGraphs />
</div>
</>
);
};

View File

@@ -1,4 +1,4 @@
import { Divider } from 'antd';
import { Button, Divider } from 'antd';
import Input from 'components/Input';
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
import React, { useCallback, useState } from 'react';
@@ -6,19 +6,22 @@ import { connect } from 'react-redux';
import { useLocation } from 'react-router';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { DeleteQuery } from 'store/actions';
import {
UpdateQuery,
UpdateQueryProps,
} from 'store/actions/dashboard/updateQuery';
import AppActions from 'types/actions';
import { DeleteQueryProps } from 'types/actions/dashboard';
import { Container, InputContainer } from './styles';
import { Container, InputContainer, QueryWrapper } from './styles';
const Query = ({
currentIndex,
preLegend,
preQuery,
updateQuery,
deleteQuery,
}: QueryProps): JSX.Element => {
const [promqlQuery, setPromqlQuery] = useState(preQuery);
const [legendFormat, setLegendFormat] = useState(preLegend);
@@ -43,33 +46,47 @@ const Query = ({
});
};
return (
<Container>
<InputContainer>
<Input
onChangeHandler={(event): void =>
onChangeHandler(setPromqlQuery, event.target.value)
}
size="middle"
value={promqlQuery}
addonBefore={'PromQL Query'}
onBlur={(): void => onBlurHandler()}
/>
</InputContainer>
const onDeleteQueryHandler = (): void => {
deleteQuery({
widgetId: widgetId,
currentIndex,
});
};
return (
<>
<Container>
<QueryWrapper>
<InputContainer>
<Input
onChangeHandler={(event): void =>
onChangeHandler(setPromqlQuery, event.target.value)
}
size="middle"
value={promqlQuery}
addonBefore={'PromQL Query'}
onBlur={(): void => onBlurHandler()}
/>
</InputContainer>
<InputContainer>
<Input
onChangeHandler={(event): void =>
onChangeHandler(setLegendFormat, event.target.value)
}
size="middle"
value={legendFormat}
addonBefore={'Legend Format'}
onBlur={(): void => onBlurHandler()}
/>
</InputContainer>
</QueryWrapper>
<Button onClick={onDeleteQueryHandler}>Delete</Button>
</Container>
<InputContainer>
<Input
onChangeHandler={(event): void =>
onChangeHandler(setLegendFormat, event.target.value)
}
size="middle"
value={legendFormat}
addonBefore={'Legend Format'}
onBlur={(): void => onBlurHandler()}
/>
</InputContainer>
<Divider />
</Container>
</>
);
};
@@ -77,12 +94,16 @@ interface DispatchProps {
updateQuery: (
props: UpdateQueryProps,
) => (dispatch: Dispatch<AppActions>) => void;
deleteQuery: (
props: DeleteQueryProps,
) => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
): DispatchProps => ({
updateQuery: bindActionCreators(UpdateQuery, dispatch),
deleteQuery: bindActionCreators(DeleteQuery, dispatch),
});
interface QueryProps extends DispatchProps {

View File

@@ -1,5 +1,4 @@
import { PlusOutlined } from '@ant-design/icons';
import Spinner from 'components/Spinner';
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
import React, { useCallback, useMemo } from 'react';
import { connect, useSelector } from 'react-redux';
@@ -47,12 +46,8 @@ const QuerySection = ({
});
}, [createQuery, urlQuery]);
if (query.length === 0) {
return <Spinner size="small" height="30vh" tip="Loading..." />;
}
return (
<div>
<>
{query.map((e, index) => (
<Query
currentIndex={index}
@@ -66,7 +61,7 @@ const QuerySection = ({
<QueryButton onClick={queryOnClickHandler} icon={<PlusOutlined />}>
Query
</QueryButton>
</div>
</>
);
};

View File

@@ -7,6 +7,7 @@ export const InputContainer = styled.div`
export const Container = styled.div`
margin-top: 1rem;
display: flex;
`;
export const QueryButton = styled(Button)`
@@ -15,3 +16,11 @@ export const QueryButton = styled(Button)`
align-items: center;
}
`;
export const QueryWrapper = styled.div`
width: 100%; // parent need to 100%
> div {
width: 95%; // each child is taking 95% of the parent
}
`;

View File

@@ -2,38 +2,23 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title data-react-helmet="true">Open source Observability platform | SigNoz</title><meta data-react-helmet="true" property="og:title" content="Open source Observability platform | SigNoz"><meta data-react-helmet="true" name="description" content="SigNoz is an opensource observability platform to help you find issues in your deployed applications &amp; solve them quickly. It provides an integrated UI for metrics and traces with deep filtering and aggregation to pin down specific issues very quickly. Built on Kafka and Druid, it is designed to handle enterprise scale."><meta data-react-helmet="true" property="og:description" content="SigNoz is an opensource observability platform to help you find issues in your deployed applications &amp; solve them quickly. It provides an integrated UI for metrics and traces with deep filtering and aggregation to pin down specific issues very quickly. Built on Kafka and Druid, it is designed to handle enterprise scale."><meta data-react-helmet="true" property="og:image" content="https://signoz.io/img/HeroShot-3.jpg"><meta data-react-helmet="true" name="twitter:image" content="https://signoz.io/img/HeroShot-3.jpg"><meta data-react-helmet="true" name="twitter:image:alt" content="Image for Open source Observability platform | SigNoz"><meta data-react-helmet="true" name="twitter:card" content="summary_large_image"><meta data-react-helmet="true" name="docusaurus_locale" content="en"><meta data-react-helmet="true" name="docusaurus_tag" content="default"><link data-react-helmet="true" rel="shortcut icon" href="/img/favicon.ico">
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<!--
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
<title data-react-helmet="true">Open source Observability platform | SigNoz</title>
<meta data-react-helmet="true" property="og:title" content="Open source Observability platform | SigNoz">
<meta data-react-helmet="true" name="description" content="SigNoz is an opensource observability platform to help you find issues in your deployed applications &amp; solve them quickly. It provides an integrated UI for metrics and traces with deep filtering and aggregation to pin down specific issues very quickly. Built on Kafka and Druid, it is designed to handle enterprise scale.">
<meta data-react-helmet="true" property="og:description" content="SigNoz is an opensource observability platform to help you find issues in your deployed applications &amp; solve them quickly. It provides an integrated UI for metrics and traces with deep filtering and aggregation to pin down specific issues very quickly. Built on Kafka and Druid, it is designed to handle enterprise scale.">
<meta data-react-helmet="true" property="og:image" content="https://signoz.io/img/HeroShot-3.jpg">
<meta data-react-helmet="true" name="twitter:image" content="https://signoz.io/img/HeroShot-3.jpg">
<meta data-react-helmet="true" name="twitter:image:alt" content="Image for Open source Observability platform | SigNoz">
<meta data-react-helmet="true" name="twitter:card" content="summary_large_image">
<meta data-react-helmet="true" name="docusaurus_locale" content="en">
<meta data-react-helmet="true" name="docusaurus_tag" content="default">
<link data-react-helmet="true" rel="shortcut icon" href="/favicon.ico">
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Signoz</title>
<link id='darkMode' rel='stylesheet' type='text/css' href='/css/antd.dark.min.css' />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@@ -2,18 +2,14 @@ import 'assets/index.css';
import AppRoutes from 'AppRoutes';
import React from 'react';
import { ThemeSwitcherProvider } from 'react-css-theme-switcher';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from 'store';
import themes from 'themes';
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<ThemeSwitcherProvider themeMap={themes} defaultTheme="dark">
<AppRoutes />
</ThemeSwitcherProvider>
<AppRoutes />
</React.StrictMode>
</Provider>,
document.querySelector('#root'),

View File

@@ -1,6 +1,6 @@
const convertDateToAmAndPm = (date: Date): string => {
return date.toLocaleString('en-US', {
hour: 'numeric',
hour: '2-digit',
minute: 'numeric',
hour12: true,
});

View File

@@ -23,11 +23,9 @@ const getChartData = ({ queryData }: GetChartDataProps): ChartData => {
};
});
const color = colors[index] || 'red';
return {
label: labelNames,
first: dataValue.map((e) => e.first),
borderColor: color,
second: dataValue.map((e) => e.second),
};
});
@@ -37,10 +35,6 @@ const getChartData = ({ queryData }: GetChartDataProps): ChartData => {
.map((e) => e.map((e) => e.label))
.reduce((a, b) => [...a, ...b], []);
const allColor = response
.map((e) => e.map((e) => e.borderColor))
.reduce((a, b) => [...a, ...b], []);
const alldata = response
.map((e) => e.map((e) => e.second))
.reduce((a, b) => [...a, ...b], []);
@@ -53,7 +47,7 @@ const getChartData = ({ queryData }: GetChartDataProps): ChartData => {
borderWidth: 1.5,
spanGaps: true,
animations: false,
borderColor: allColor[index],
borderColor: colors[index] || 'red',
showLine: true,
pointRadius: 0,
};

View File

@@ -0,0 +1,13 @@
function getFormattedDate(date: Date): string {
const year = date.getFullYear();
let month = (1 + date.getMonth()).toString();
month = month.length > 1 ? month : '0' + month;
let day = date.getDate().toString();
day = day.length > 1 ? day : '0' + day;
return month + '/' + day + '/' + year;
}
export default getFormattedDate;

View File

@@ -1,25 +1,22 @@
import { Space } from 'antd';
import React from 'react';
import { useThemeSwitcher } from 'react-css-theme-switcher';
import { connect } from 'react-redux';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import styled from 'styled-components';
import AppReducer from 'types/reducer/app';
const InstrumentCard = styled.div<{
currentThemeStatus: string | undefined;
isDarkMode: boolean;
}>`
border-radius: 4px;
background: ${({ currentThemeStatus }): string =>
currentThemeStatus === 'dark' ? '#313131' : '#ddd'};
background: ${({ isDarkMode }): string => (isDarkMode ? '#313131' : '#ddd')};
padding: 33px 23px;
max-width: 800px;
margin-top: 40px;
`;
interface InstrumentationPageProps {}
const InstrumentationPage = (props: InstrumentationPageProps) => {
const { currentTheme } = useThemeSwitcher();
const InstrumentationPage = (): JSX.Element => {
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
return (
<React.Fragment>
@@ -27,7 +24,7 @@ const InstrumentationPage = (props: InstrumentationPageProps) => {
<div>
<h2>Instrument your application</h2>
</div>
<InstrumentCard currentThemeStatus={currentTheme}>
<InstrumentCard isDarkMode={isDarkMode}>
Congrats, you have successfully installed SigNoz!
<br />
To start seeing YOUR application data here, follow the instructions in the
@@ -61,8 +58,4 @@ const InstrumentationPage = (props: InstrumentationPageProps) => {
);
};
const mapStateToProps = (state: AppState): {} => {
return {};
};
export default connect(mapStateToProps, {})(InstrumentationPage);
export default InstrumentationPage;

View File

@@ -0,0 +1 @@
export * from './toggleDarkMode';

View File

@@ -0,0 +1,12 @@
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
export const ToggleDarkMode = (): ((
dispatch: Dispatch<AppActions>,
) => void) => {
return (dispatch: Dispatch<AppActions>): void => {
dispatch({
type: 'SWITCH_DARK_MODE',
});
};
};

View File

@@ -0,0 +1,17 @@
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
import { DeleteQueryProps } from 'types/actions/dashboard';
export const DeleteQuery = (
props: DeleteQueryProps,
): ((dispatch: Dispatch<AppActions>) => void) => {
return (dispatch: Dispatch<AppActions>): void => {
dispatch({
type: 'DELETE_QUERY',
payload: {
currentIndex: props.currentIndex,
widgetId: props.widgetId,
},
});
};
};

View File

@@ -1,6 +1,7 @@
export * from './applySettingsToPanel';
export * from './createQuery';
export * from './deleteDashboard';
export * from './deleteQuery';
export * from './getAllDashboard';
export * from './getDashboard';
export * from './toggleEditMode';

View File

@@ -1,3 +1,4 @@
export * from './app';
export * from './dashboard';
export * from './global';
export * from './MetricsActions';

View File

@@ -0,0 +1,25 @@
import { AppAction, SWITCH_DARK_MODE } from 'types/actions/app';
import InitialValueTypes from 'types/reducer/app';
const InitialValue: InitialValueTypes = {
isDarkMode: true,
};
const appReducer = (
state = InitialValue,
action: AppAction,
): InitialValueTypes => {
switch (action.type) {
case SWITCH_DARK_MODE: {
return {
...state,
isDarkMode: !state.isDarkMode,
};
}
default:
return state;
}
};
export default appReducer;

View File

@@ -4,6 +4,7 @@ import {
CREATE_NEW_QUERY,
DashboardActions,
DELETE_DASHBOARD_SUCCESS,
DELETE_QUERY,
DELETE_WIDGET_SUCCESS,
GET_ALL_DASHBOARD_ERROR,
GET_ALL_DASHBOARD_LOADING_START,
@@ -438,6 +439,50 @@ const dashboard = (
],
};
}
case DELETE_QUERY: {
const { currentIndex, widgetId } = action.payload;
const { dashboards } = state;
const [selectedDashboard] = dashboards;
const { data } = selectedDashboard;
const { widgets = [] } = data;
const selectedWidgetIndex = widgets.findIndex((e) => e.id === widgetId) || 0;
const preWidget = widgets?.slice(0, selectedWidgetIndex) || [];
const afterWidget =
widgets?.slice(
selectedWidgetIndex + 1, // this is never undefined
widgets.length,
) || [];
const selectedWidget = widgets[selectedWidgetIndex];
const query = selectedWidget.query;
const preQuery = query.slice(0, currentIndex);
const postQuery = query.slice(currentIndex + 1, query.length);
return {
...state,
dashboards: [
{
...selectedDashboard,
data: {
...data,
widgets: [
...preWidget,
{
...selectedWidget,
query: [...preQuery, ...postQuery],
},
...afterWidget,
],
},
},
],
};
}
default:
return state;
}

View File

@@ -6,7 +6,7 @@ export const updateGlobalTimeReducer = (
minTime: (Date.now() - 15 * 60 * 1000) * 1000000,
},
action: Action,
) => {
): GlobalTime => {
// Initial global state is time now and 15 minute interval
switch (action.type) {
case ActionTypes.updateTimeInterval:

View File

@@ -1,5 +1,6 @@
import { combineReducers } from 'redux';
import appReducer from './app';
import dashboardReducer from './dashboard';
import { updateGlobalTimeReducer } from './global';
import { metricsReducer } from './metrics';
@@ -17,6 +18,7 @@ const reducers = combineReducers({
metricsData: metricsReducer,
serviceMap: ServiceMapReducer,
dashboards: dashboardReducer,
app: appReducer,
});
export type AppState = ReturnType<typeof reducers>;

View File

@@ -1,6 +0,0 @@
const themes = {
dark: '/dark-theme.css',
light: '/light-theme.css',
};
export default themes;

View File

@@ -0,0 +1,7 @@
export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE';
export interface SwitchDarkMode {
type: typeof SWITCH_DARK_MODE;
}
export type AppAction = SwitchDarkMode;

View File

@@ -40,6 +40,8 @@ export const DELETE_WIDGET_ERROR = 'DELETE_WIDGET_ERROR';
export const IS_ADD_WIDGET = 'IS_ADD_WIDGET';
export const DELETE_QUERY = 'DELETE_QUERY';
interface GetDashboard {
type: typeof GET_DASHBOARD;
payload: Dashboard;
@@ -159,6 +161,16 @@ interface WidgetDeleteSuccess {
};
}
export interface DeleteQueryProps {
widgetId: string;
currentIndex: number;
}
interface DeleteQuery {
type: typeof DELETE_QUERY;
payload: DeleteQueryProps;
}
export type DashboardActions =
| GetDashboard
| UpdateDashboard
@@ -177,4 +189,5 @@ export type DashboardActions =
| SaveDashboardSuccess
| WidgetDeleteSuccess
| IsAddWidget
| UpdateQuery;
| UpdateQuery
| DeleteQuery;

View File

@@ -1,5 +1,6 @@
import { AppAction } from './app';
import { DashboardActions } from './dashboard';
type AppActions = DashboardActions;
type AppActions = DashboardActions | AppAction;
export default AppActions;

View File

@@ -1,5 +1,6 @@
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
import { Layout } from 'react-grid-layout';
import { QueryData } from '../widgets/getQuery';
@@ -19,6 +20,7 @@ export interface DashboardData {
name?: string;
widgets?: Widgets[];
title: string;
layout?: Layout[];
}
export interface Widgets {

View File

@@ -0,0 +1,3 @@
export default interface AppReducer {
isDarkMode: boolean;
}

View File

@@ -11946,11 +11946,6 @@ react-chips@^0.8.0:
react-autosuggest "^9.0.1"
react-themeable "^1.1.0"
react-css-theme-switcher@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/react-css-theme-switcher/-/react-css-theme-switcher-0.1.6.tgz#9b765e4ffa7d1ce092ff11c5524b1466990e9eaf"
integrity sha512-GG5OqeWTWJFH3Vbd42Tj0QVUwh/uBN/ki7EOTUL99wbzxulX8bof3NOdRHzY6j7746HNZ72s8Ko4TjO3GaWdxA==
react-dev-utils@^11.0.0:
version "11.0.4"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a"

View File

@@ -57,6 +57,7 @@ func NewReader() *ClickHouseReader {
if err != nil {
zap.S().Error(err)
os.Exit(1)
}
logLevel := promlog.AllowedLevel{}

View File

@@ -52,7 +52,7 @@ func NewAPIHandler(reader *Reader, pc *posthog.Client, distinctId string) (*APIH
}
aH.ready = aH.testReady
err := dashboards.InitDB("signoz.db")
err := dashboards.InitDB("/var/lib/signoz/signoz.db")
if err != nil {
return nil, err
}