Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5fd338a9d | ||
|
|
8a781076e1 | ||
|
|
18fc697b91 | ||
|
|
93b347d25e | ||
|
|
ea5b40c7ea | ||
|
|
cc91242e9a | ||
|
|
e756cefa75 | ||
|
|
da653681cf | ||
|
|
93b5a945a4 | ||
|
|
9ab1093d81 | ||
|
|
b4754053aa | ||
|
|
8fef964485 | ||
|
|
004dda200c | ||
|
|
6a01ce88cb |
517
deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml
Normal file
517
deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml
Normal 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>
|
||||
113
deploy/docker-swarm/clickhouse-setup/docker-compose.yaml
Normal file
113
deploy/docker-swarm/clickhouse-setup/docker-compose.yaml
Normal 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
|
||||
@@ -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))
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
25
deploy/docker-swarm/clickhouse-setup/prometheus.yml
Normal file
25
deploy/docker-swarm/clickhouse-setup/prometheus.yml
Normal 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
|
||||
16
deploy/docker-swarm/common/locust-scripts/locustfile.py
Normal file
16
deploy/docker-swarm/common/locust-scripts/locustfile.py
Normal 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")
|
||||
30
deploy/docker-swarm/common/nginx-config.conf
Normal file
30
deploy/docker-swarm/common/nginx-config.conf
Normal 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;
|
||||
}
|
||||
}
|
||||
0
deploy/docker-swarm/dashboards/.gitkeep
Normal file
0
deploy/docker-swarm/dashboards/.gitkeep
Normal 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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
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
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 |
@@ -1,5 +1,3 @@
|
||||
@import '~antd/dist/antd.dark.css';
|
||||
|
||||
.ant-space-item {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -5,10 +5,10 @@ import GridGraphs from './GridGraphs';
|
||||
|
||||
const NewDashboard = (): JSX.Element => {
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<Description />
|
||||
<GridGraphs />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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 & 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 & 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 & 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 & 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>
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const convertDateToAmAndPm = (date: Date): string => {
|
||||
return date.toLocaleString('en-US', {
|
||||
hour: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: 'numeric',
|
||||
hour12: true,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
13
frontend/src/lib/getFormatedDate.ts
Normal file
13
frontend/src/lib/getFormatedDate.ts
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
1
frontend/src/store/actions/app/index.ts
Normal file
1
frontend/src/store/actions/app/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './toggleDarkMode';
|
||||
12
frontend/src/store/actions/app/toggleDarkMode.ts
Normal file
12
frontend/src/store/actions/app/toggleDarkMode.ts
Normal 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',
|
||||
});
|
||||
};
|
||||
};
|
||||
17
frontend/src/store/actions/dashboard/deleteQuery.ts
Normal file
17
frontend/src/store/actions/dashboard/deleteQuery.ts
Normal 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,
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -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';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './app';
|
||||
export * from './dashboard';
|
||||
export * from './global';
|
||||
export * from './MetricsActions';
|
||||
|
||||
25
frontend/src/store/reducers/app.ts
Normal file
25
frontend/src/store/reducers/app.ts
Normal 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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
const themes = {
|
||||
dark: '/dark-theme.css',
|
||||
light: '/light-theme.css',
|
||||
};
|
||||
|
||||
export default themes;
|
||||
7
frontend/src/types/actions/app.ts
Normal file
7
frontend/src/types/actions/app.ts
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AppAction } from './app';
|
||||
import { DashboardActions } from './dashboard';
|
||||
|
||||
type AppActions = DashboardActions;
|
||||
type AppActions = DashboardActions | AppAction;
|
||||
|
||||
export default AppActions;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
3
frontend/src/types/reducer/app.ts
Normal file
3
frontend/src/types/reducer/app.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default interface AppReducer {
|
||||
isDarkMode: boolean;
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -57,6 +57,7 @@ func NewReader() *ClickHouseReader {
|
||||
|
||||
if err != nil {
|
||||
zap.S().Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logLevel := promlog.AllowedLevel{}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user