Merge pull request #4009 from SigNoz/release/v0.34.1

Release/v0.34.1
This commit is contained in:
Prashant Shahi 2023-11-21 18:25:01 +05:30 committed by GitHub
commit 93a11b2031
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
565 changed files with 22031 additions and 2672 deletions

View File

@ -146,7 +146,7 @@ services:
condition: on-failure
query-service:
image: signoz/query-service:0.34.0
image: signoz/query-service:0.34.1
command:
[
"-config=/root/config/prometheus.yml",
@ -186,7 +186,7 @@ services:
<<: *db-depend
frontend:
image: signoz/frontend:0.34.0
image: signoz/frontend:0.34.1
deploy:
restart_policy:
condition: on-failure
@ -199,7 +199,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
image: signoz/signoz-otel-collector:0.88.0
image: signoz/signoz-otel-collector:0.88.1
command:
[
"--config=/etc/otel-collector-config.yaml",
@ -237,7 +237,7 @@ services:
- query-service
otel-collector-migrator:
image: signoz/signoz-schema-migrator:0.88.0
image: signoz/signoz-schema-migrator:0.88.1
deploy:
restart_policy:
condition: on-failure
@ -250,7 +250,7 @@ services:
# - clickhouse-3
otel-collector-metrics:
image: signoz/signoz-otel-collector:0.88.0
image: signoz/signoz-otel-collector:0.88.1
command:
[
"--config=/etc/otel-collector-metrics-config.yaml",

View File

@ -66,7 +66,7 @@ services:
- --storage.path=/data
otel-collector-migrator:
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.0}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.1}
container_name: otel-migrator
command:
- "--dsn=tcp://clickhouse:9000"
@ -81,7 +81,7 @@ services:
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
otel-collector:
container_name: signoz-otel-collector
image: signoz/signoz-otel-collector:0.88.0
image: signoz/signoz-otel-collector:0.88.1
command:
[
"--config=/etc/otel-collector-config.yaml",
@ -118,7 +118,7 @@ services:
otel-collector-metrics:
container_name: signoz-otel-collector-metrics
image: signoz/signoz-otel-collector:0.88.0
image: signoz/signoz-otel-collector:0.88.1
command:
[
"--config=/etc/otel-collector-metrics-config.yaml",

View File

@ -164,7 +164,7 @@ services:
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
query-service:
image: signoz/query-service:${DOCKER_TAG:-0.34.0}
image: signoz/query-service:${DOCKER_TAG:-0.34.1}
container_name: signoz-query-service
command:
[
@ -203,7 +203,7 @@ services:
<<: *db-depend
frontend:
image: signoz/frontend:${DOCKER_TAG:-0.34.0}
image: signoz/frontend:${DOCKER_TAG:-0.34.1}
container_name: signoz-frontend
restart: on-failure
depends_on:
@ -215,7 +215,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector-migrator:
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.0}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.1}
container_name: otel-migrator
command:
- "--dsn=tcp://clickhouse:9000"
@ -229,7 +229,7 @@ services:
otel-collector:
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.0}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.1}
container_name: signoz-otel-collector
command:
[
@ -269,7 +269,7 @@ services:
condition: service_healthy
otel-collector-metrics:
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.0}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.1}
container_name: signoz-otel-collector-metrics
command:
[

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="2500" height="2500"><style>.st0{fill:#f3bd19}.st1{fill:#231f20}.st2{fill:#3ebeb0}.st3{fill:#37a595}.st4{fill:none}</style><path class="st0" d="M41.1 41.9H15.6V12.5h7.7c9.9 0 17.8 8 17.8 17.8v11.6z"/><path class="st1" d="M41.1 67.5c-14.1 0-25.6-11.4-25.6-25.6h25.6v25.6z"/><path class="st2" d="M41.1 41.9h23.3v25.6H41.1z"/><path class="st3" d="M41.1 41.9h5.4v25.6h-5.4z"/><path class="st4" d="M0 0h80v80H0z"/></svg>

After

Width:  |  Height:  |  Size: 494 B

View File

@ -5,6 +5,7 @@
"create": "Create",
"reorder": "Reorder",
"cancel": "Cancel",
"learn_more": "Learn more about pipelines",
"reorder_pipeline": "Do you want to reorder pipeline?",
"reorder_pipeline_description": "Logs are processed sequentially in processors and pipelines. Reordering it may change how data is processed by them.",
"delete_pipeline": "Do you want to delete pipeline",

View File

@ -7,6 +7,7 @@ import { FeatureKeys } from 'constants/features';
import { LOCALSTORAGE } from 'constants/localStorage';
import ROUTES from 'constants/routes';
import AppLayout from 'container/AppLayout';
import useAnalytics from 'hooks/analytics/useAnalytics';
import { useThemeConfig } from 'hooks/useDarkMode';
import useGetFeatureFlag from 'hooks/useGetFeatureFlag';
import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
@ -25,7 +26,6 @@ import AppActions from 'types/actions';
import { UPDATE_FEATURE_FLAG_RESPONSE } from 'types/actions/app';
import AppReducer, { User } from 'types/reducer/app';
import { extractDomain, isCloudUser, isEECloudUser } from 'utils/app';
import { trackPageView } from 'utils/segmentAnalytics';
import PrivateRoute from './Private';
import defaultRoutes, { AppRoutes, SUPPORT_ROUTE } from './routes';
@ -41,6 +41,8 @@ function App(): JSX.Element {
const dispatch = useDispatch<Dispatch<AppActions>>();
const { trackPageView } = useAnalytics();
const { hostname, pathname } = window.location;
const isCloudUserVal = isCloudUser();
@ -156,6 +158,7 @@ function App(): JSX.Element {
useEffect(() => {
trackPageView(pathname);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pathname]);
return (

View File

@ -220,7 +220,11 @@ function ExplorerCard({
open={isOpen}
onOpenChange={handleOpenChange}
>
<Button type={saveButtonType} icon={saveButtonIcon}>
<Button
type={saveButtonType}
icon={saveButtonIcon}
data-testid="traces-save-view-action"
>
{isQueryUpdated
? SaveButtonText.SAVE_AS_NEW_VIEW
: SaveButtonText.SAVE_VIEW}

View File

@ -64,7 +64,12 @@ function SaveViewWithName({
>
<Input placeholder="Enter Name" />
</Form.Item>
<SaveButton htmlType="submit" type="primary" loading={isLoading}>
<SaveButton
htmlType="submit"
type="primary"
loading={isLoading}
data-testid="save-view-name-action-button"
>
Save
</SaveButton>
</Form>

View File

@ -1,6 +1,8 @@
.code-snippet-container {
position: relative;
background-color: rgb(43, 43, 43);
// background-color: rgb(43, 43, 43);
background-color: #111a2c;
border-color: #111a2c;
}
.code-copy-btn {

View File

@ -3,6 +3,7 @@ import './uplot.scss';
import { Typography } from 'antd';
import { ToggleGraphProps } from 'components/Graph/types';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import {
forwardRef,
memo,
@ -11,6 +12,7 @@ import {
useImperativeHandle,
useRef,
} from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import UPlot from 'uplot';
import { dataMatch, optionsUpdateState } from './utils';
@ -119,6 +121,7 @@ const Uplot = forwardRef<ToggleGraphProps | undefined, UplotProps>(
}, [data, resetScales, create]);
return (
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
<div className="uplot-graph-container" ref={targetRef}>
{data && data[0] && data[0]?.length === 0 ? (
<div className="not-found">
@ -126,6 +129,7 @@ const Uplot = forwardRef<ToggleGraphProps | undefined, UplotProps>(
</div>
) : null}
</div>
</ErrorBoundary>
);
},
);

View File

@ -5,7 +5,7 @@ export const Layout = styled(LayoutComponent)`
&&& {
display: flex;
position: relative;
min-height: calc(100vh - 4rem);
min-height: calc(100vh - 8rem);
overflow: hidden;
height: 100%;
}

View File

@ -9,6 +9,7 @@ import getUsage from 'api/billing/getUsage';
import manageCreditCardApi from 'api/billing/manage';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import useAnalytics from 'hooks/analytics/useAnalytics';
import useAxiosError from 'hooks/useAxiosError';
import useLicense from 'hooks/useLicense';
import { useNotifications } from 'hooks/useNotifications';
@ -109,9 +110,11 @@ export default function BillingContainer(): JSX.Element {
const [data, setData] = useState<any[]>([]);
const billCurrency = '$';
const { trackEvent } = useAnalytics();
const { isFetching, data: licensesData, error: licenseError } = useLicense();
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
const { user, org } = useSelector<AppState, AppReducer>((state) => state.app);
const { notifications } = useNotifications();
const handleError = useAxiosError();
@ -301,18 +304,29 @@ export default function BillingContainer(): JSX.Element {
const handleBilling = useCallback(async () => {
if (isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription) {
trackEvent('Billing : Upgrade Plan', {
user,
org,
});
updateCreditCard({
licenseKey: activeLicense?.key || '',
successURL: window.location.href,
cancelURL: window.location.href,
});
} else {
trackEvent('Billing : Manage Billing', {
user,
org,
});
manageCreditCard({
licenseKey: activeLicense?.key || '',
successURL: window.location.href,
cancelURL: window.location.href,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
activeLicense?.key,
isFreeTrial,
@ -432,7 +446,12 @@ export default function BillingContainer(): JSX.Element {
</Typography.Text>
</Col>
<Col span={4} style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button type="primary" size="middle">
<Button
type="primary"
size="middle"
loading={isLoadingBilling || isLoadingManageBilling}
onClick={handleBilling}
>
Upgrade Plan
</Button>
</Col>

View File

@ -7,8 +7,8 @@ import { Time } from 'container/TopNav/DateTimeSelection/config';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

View File

@ -15,8 +15,8 @@ import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
import { useChartMutable } from 'hooks/useChartMutable';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useSelector } from 'react-redux';

View File

@ -5,8 +5,8 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { useIntersectionObserver } from 'hooks/useIntersectionObserver';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import isEmpty from 'lodash-es/isEmpty';
import _noop from 'lodash-es/noop';
import { memo, useCallback, useMemo, useRef, useState } from 'react';
@ -28,6 +28,7 @@ function GridCardGraph({
isQueryEnabled,
threshold,
variables,
filterNaN,
}: GridCardGraphProps): JSX.Element {
const dispatch = useDispatch();
const [errorMessage, setErrorMessage] = useState<string>();
@ -89,7 +90,11 @@ function GridCardGraph({
const containerDimensions = useResizeObserver(graphRef);
const chartData = getUPlotChartData(queryResponse?.data?.payload);
const chartData = getUPlotChartData(
queryResponse?.data?.payload,
undefined,
filterNaN,
);
const isDarkMode = useIsDarkMode();

View File

@ -39,6 +39,7 @@ export interface GridCardGraphProps {
headerMenuList?: WidgetGraphComponentProps['headerMenuList'];
isQueryEnabled: boolean;
variables?: Dashboard['data']['variables'];
filterNaN?: boolean;
}
export interface GetGraphVisibilityStateOnLegendClickProps {

View File

@ -118,7 +118,11 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
)}
{addPanelPermission && (
<Button onClick={onAddPanelHandler} icon={<PlusOutlined />}>
<Button
onClick={onAddPanelHandler}
icon={<PlusOutlined />}
data-testid="add-panel"
>
{t('dashboard:add_panel')}
</Button>
)}

View File

@ -203,7 +203,7 @@ function WidgetHeader({
onClick={onClickHandler}
>
<HeaderContentContainer>
<Typography.Text style={{ maxWidth: '80%' }} ellipsis>
<Typography.Text style={{ maxWidth: '80%' }} ellipsis data-testid={title}>
{title}
</Typography.Text>
<ArrowContainer hover={parentHover}>

View File

@ -135,14 +135,13 @@ function HeaderContainer(): JSX.Element {
<>
{showTrialExpiryBanner && (
<div className="trial-expiry-banner">
You are in free trial period. Your free trial will end on{' '}
You are in free trial period. Your free trial will end on
<span>
{getFormattedDate(licenseData?.payload?.trialEnd || Date.now())}.
</span>
{role === 'ADMIN' ? (
<span>
{' '}
Please{' '}
Please
<Button className="upgrade-link" type="link" onClick={handleUpgrade}>
upgrade
</Button>

View File

@ -3,6 +3,8 @@ import styled from 'styled-components';
export const Header = styled(Layout.Header)`
background: #1f1f1f !important;
padding-left: 16px;
padding-right: 16px;
`;
export const Container = styled.div`

View File

@ -7,14 +7,14 @@ import ApplyLicenseForm from './ApplyLicenseForm';
import ListLicenses from './ListLicenses';
function Licenses(): JSX.Element {
const { t } = useTranslation(['licenses']);
const { t, ready: translationsReady } = useTranslation(['licenses']);
const { data, isError, isLoading, refetch } = useLicense();
if (isError || data?.error) {
return <Typography>{data?.error}</Typography>;
}
if (isLoading || data?.payload === undefined) {
if (isLoading || data?.payload === undefined || !translationsReady) {
return <Spinner tip={t('loading_licenses')} height="90vh" />;
}

View File

@ -1,5 +1,5 @@
import { DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import { Modal, Tooltip } from 'antd';
import { Modal, Tooltip, Typography } from 'antd';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { useDeleteDashboard } from 'hooks/dashboard/useDeleteDashboard';
import { useCallback } from 'react';
@ -10,10 +10,22 @@ import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
import { USER_ROLES } from 'types/roles';
import { Data } from '../index';
import { Data } from '..';
import { TableLinkText } from './styles';
function DeleteButton({ id, createdBy, isLocked }: Data): JSX.Element {
interface DeleteButtonProps {
createdBy: string;
name: string;
id: string;
isLocked: boolean;
}
function DeleteButton({
createdBy,
name,
id,
isLocked,
}: DeleteButtonProps): JSX.Element {
const [modal, contextHolder] = Modal.useModal();
const { role, user } = useSelector<AppState, AppReducer>((state) => state.app);
const isAuthor = user?.email === createdBy;
@ -26,7 +38,13 @@ function DeleteButton({ id, createdBy, isLocked }: Data): JSX.Element {
const openConfirmationDialog = useCallback((): void => {
modal.confirm({
title: 'Do you really want to delete this dashboard?',
title: (
<Typography.Title level={5}>
Are you sure you want to delete the
<span style={{ color: '#e42b35', fontWeight: 500 }}> {name} </span>
dashboard?
</Typography.Title>
),
icon: <ExclamationCircleOutlined style={{ color: '#e42b35' }} />,
onOk() {
deleteDashboardMutation.mutateAsync(undefined, {
@ -39,7 +57,7 @@ function DeleteButton({ id, createdBy, isLocked }: Data): JSX.Element {
okButtonProps: { danger: true },
centered: true,
});
}, [modal, deleteDashboardMutation, queryClient]);
}, [modal, name, deleteDashboardMutation, queryClient]);
const getDeleteTooltipContent = (): string => {
if (isLocked) {

View File

@ -1,11 +1,12 @@
import { PlusOutlined } from '@ant-design/icons';
import {
Card,
Col,
Dropdown,
Input,
MenuProps,
Row,
TableColumnProps,
Typography,
} from 'antd';
import { ItemType } from 'antd/es/menu/hooks/useItems';
import createDashboard from 'api/dashboard/create';
@ -18,9 +19,9 @@ import DynamicColumnTable from 'components/ResizeTable/DynamicColumnTable';
import LabelColumn from 'components/TableRenderer/LabelColumn';
import TextToolTip from 'components/TextToolTip';
import ROUTES from 'constants/routes';
import SearchFilter from 'container/ListOfDashboard/SearchFilter';
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
import useComponentPermission from 'hooks/useComponentPermission';
import useDebouncedFn from 'hooks/useDebouncedFunction';
import history from 'lib/history';
import { Key, useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -38,6 +39,8 @@ import DeleteButton from './TableComponents/DeleteButton';
import Name from './TableComponents/Name';
function ListOfAllDashboard(): JSX.Element {
const { Search } = Input;
const {
data: dashboardListResponse = [],
isLoading: isDashboardListLoading,
@ -59,12 +62,21 @@ function ListOfAllDashboard(): JSX.Element {
] = useState<boolean>(false);
const [uploadedGrafana, setUploadedGrafana] = useState<boolean>(false);
const [isFilteringDashboards, setIsFilteringDashboards] = useState(false);
const [filteredDashboards, setFilteredDashboards] = useState<Dashboard[]>();
const [dashboards, setDashboards] = useState<Dashboard[]>();
const sortDashboardsByCreatedAt = (dashboards: Dashboard[]): void => {
const sortedDashboards = dashboards.sort(
(a, b) =>
new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),
);
setDashboards(sortedDashboards);
};
useEffect(() => {
if (dashboardListResponse.length) {
setFilteredDashboards(dashboardListResponse);
sortDashboardsByCreatedAt(dashboardListResponse);
}
}, [dashboardListResponse]);
@ -150,7 +162,7 @@ function ListOfAllDashboard(): JSX.Element {
}, [action]);
const data: Data[] =
filteredDashboards?.map((e) => ({
dashboards?.map((e) => ({
createdAt: e.created_at,
description: e.data.description || '',
id: e.uuid,
@ -255,11 +267,50 @@ function ListOfAllDashboard(): JSX.Element {
[getMenuItems],
);
const searchArrayOfObjects = (searchValue: string): any[] => {
// Convert the searchValue to lowercase for case-insensitive search
const searchValueLowerCase = searchValue.toLowerCase();
// Use the filter method to find matching objects
return dashboardListResponse.filter((item: any) => {
// Convert each property value to lowercase for case-insensitive search
const itemValues = Object.values(item?.data).map((value: any) =>
value.toString().toLowerCase(),
);
// Check if any property value contains the searchValue
return itemValues.some((value) => value.includes(searchValueLowerCase));
});
};
const handleSearch = useDebouncedFn((event: unknown): void => {
setIsFilteringDashboards(true);
const searchText = (event as React.BaseSyntheticEvent)?.target?.value || '';
const filteredDashboards = searchArrayOfObjects(searchText);
setDashboards(filteredDashboards);
setIsFilteringDashboards(false);
}, 500);
const GetHeader = useMemo(
() => (
<Row justify="space-between">
<Typography>Dashboard List</Typography>
<Row gutter={16} align="middle">
<Col span={18}>
<Search
disabled={isDashboardListLoading}
placeholder="Search by Name, Description, Tags"
onChange={handleSearch}
loading={isFilteringDashboards}
style={{ marginBottom: 16, marginTop: 16 }}
/>
</Col>
<Col
span={6}
style={{
display: 'flex',
justifyContent: 'flex-end',
}}
>
<ButtonContainer>
<TextToolTip
{...{
@ -277,6 +328,7 @@ function ListOfAllDashboard(): JSX.Element {
<NewDashboardButton
icon={<PlusOutlined />}
type="primary"
data-testid="create-new-dashboard"
loading={newDashboardState.loading}
danger={newDashboardState.error}
>
@ -285,11 +337,15 @@ function ListOfAllDashboard(): JSX.Element {
</Dropdown>
)}
</ButtonContainer>
</Col>
</Row>
),
[
newDashboard,
Search,
isDashboardListLoading,
handleSearch,
isFilteringDashboards,
newDashboard,
menu,
newDashboardState.loading,
newDashboardState.error,
@ -301,13 +357,6 @@ function ListOfAllDashboard(): JSX.Element {
<Card>
{GetHeader}
{!isDashboardListLoading && (
<SearchFilter
searchData={dashboardListResponse}
filterDashboards={setFilteredDashboards}
/>
)}
<TableContainer>
<ImportJSON
isImportJSONModalVisible={isImportJSONModalVisible}
@ -319,8 +368,9 @@ function ListOfAllDashboard(): JSX.Element {
dynamicColumns={dynamicColumns}
columns={columns}
pagination={{
pageSize: 9,
defaultPageSize: 9,
pageSize: 10,
defaultPageSize: 10,
total: data?.length || 0,
}}
showHeader
bordered

View File

@ -41,6 +41,7 @@ import {
import { DataSource, LogsAggregatorOperator } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime';
import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink';
import { v4 } from 'uuid';
import { ActionsWrapper } from './LogsExplorerViews.styled';
@ -268,9 +269,12 @@ function LogsExplorerViews(): JSX.Element {
? panelType
: PANEL_TYPES.TIME_SERIES;
const widgetId = v4();
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
dashboard,
exportDefaultQuery,
widgetId,
panelTypeParam,
);
@ -304,6 +308,7 @@ function LogsExplorerViews(): JSX.Element {
query: exportDefaultQuery,
panelType: panelTypeParam,
dashboardId: data.payload?.uuid || '',
widgetId,
});
history.push(dashboardEditView);

View File

@ -107,6 +107,7 @@ function DBCall(): JSX.Element {
<Card data-testid="database_call_rps">
<GraphContainer>
<Graph
filterNaN
name="database_call_rps"
widget={databaseCallsRPSWidget}
onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
@ -140,6 +141,7 @@ function DBCall(): JSX.Element {
<Card data-testid="database_call_avg_duration">
<GraphContainer>
<Graph
filterNaN
name="database_call_avg_duration"
widget={databaseCallsAverageDurationWidget}
headerMenuList={MENU_ITEMS}

View File

@ -148,6 +148,7 @@ function External(): JSX.Element {
<Card data-testid="external_call_error_percentage">
<GraphContainer>
<Graph
filterNaN
headerMenuList={MENU_ITEMS}
name="external_call_error_percentage"
widget={externalCallErrorWidget}
@ -183,6 +184,7 @@ function External(): JSX.Element {
<Card data-testid="external_call_duration">
<GraphContainer>
<Graph
filterNaN
name="external_call_duration"
headerMenuList={MENU_ITEMS}
widget={externalCallDurationWidget}
@ -219,6 +221,7 @@ function External(): JSX.Element {
<Card data-testid="external_call_rps_by_address">
<GraphContainer>
<Graph
filterNaN
name="external_call_rps_by_address"
widget={externalCallRPSWidget}
headerMenuList={MENU_ITEMS}
@ -257,6 +260,7 @@ function External(): JSX.Element {
name="external_call_duration_by_address"
widget={externalCallDurationAddressWidget}
headerMenuList={MENU_ITEMS}
filterNaN
onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
onGraphClickHandler(setSelectedTimeStamp)(
xValue,

View File

@ -84,6 +84,7 @@ function ApDexMetrics({
return (
<Graph
name="apdex"
filterNaN
widget={apDexMetricsWidget}
onDragSelect={onDragSelect}
onClickHandler={handleGraphClick('ApDex')}

View File

@ -88,6 +88,7 @@ function ServiceOverview({
widget={latencyWidget}
onClickHandler={handleGraphClick('Service')}
isQueryEnabled={isQueryEnabled}
filterNaN
/>
</GraphContainer>
</Card>

View File

@ -27,6 +27,7 @@ function TopLevelOperation({
) : (
<GraphContainer>
<Graph
filterNaN
name={name}
widget={widget}
onClickHandler={handleGraphClick(opName)}

View File

@ -18,7 +18,12 @@ function SettingsDrawer({ drawerTitle }: { drawerTitle: string }): JSX.Element {
return (
<>
<Button type="dashed" onClick={showDrawer} style={{ width: '100%' }}>
<Button
type="dashed"
onClick={showDrawer}
style={{ width: '100%' }}
data-testid="show-drawer"
>
<SettingOutlined /> Configure
</Button>
<DrawerContainer

View File

@ -51,7 +51,11 @@ function DashboardDescription(): JSX.Element {
<Card>
<Row gutter={16}>
<Col flex={1} span={12}>
<Typography.Title level={4} style={{ padding: 0, margin: 0 }}>
<Typography.Title
level={4}
style={{ padding: 0, margin: 0 }}
data-testid="dashboard-landing-name"
>
{isDashboardLocked && (
<Tooltip title="Dashboard Locked" placement="top">
<LockFilled /> &nbsp;
@ -60,7 +64,12 @@ function DashboardDescription(): JSX.Element {
{title}
</Typography.Title>
{description && (
<Typography className="dashboard-description">{description}</Typography>
<Typography
className="dashboard-description"
data-testid="dashboard-landing-desc"
>
{description}
</Typography>
)}
{tags && (

View File

@ -63,6 +63,7 @@ function GeneralDashboardSettings(): JSX.Element {
<div>
<Typography style={{ marginBottom: '0.5rem' }}>Name</Typography>
<Input
data-testid="dashboard-name"
value={updatedTitle}
onChange={(e): void => setUpdatedTitle(e.target.value)}
/>
@ -71,6 +72,7 @@ function GeneralDashboardSettings(): JSX.Element {
<div>
<Typography style={{ marginBottom: '0.5rem' }}>Description</Typography>
<Input.TextArea
data-testid="dashboard-desc"
rows={5}
value={updatedDescription}
onChange={(e): void => setUpdatedDescription(e.target.value)}
@ -88,6 +90,7 @@ function GeneralDashboardSettings(): JSX.Element {
disabled={updateDashboardMutation.isLoading}
loading={updateDashboardMutation.isLoading}
icon={<SaveOutlined />}
data-testid="save-dashboard-config"
onClick={onSaveHandler}
type="primary"
>

View File

@ -181,6 +181,7 @@ function VariablesSetting(): JSX.Element {
<>
<Row style={{ flexDirection: 'row-reverse', padding: '0.5rem 0' }}>
<Button
data-testid="add-new-variable"
type="primary"
onClick={(): void =>
onVariableViewModeEnter('ADD', {} as IDashboardVariable)

View File

@ -3,12 +3,16 @@ import { Tabs } from 'antd';
import GeneralDashboardSettings from './General';
import VariablesSetting from './Variables';
const items = [
{ label: 'General', key: 'general', children: <GeneralDashboardSettings /> },
{ label: 'Variables', key: 'variables', children: <VariablesSetting /> },
];
function DashboardSettingsContent(): JSX.Element {
const items = [
{
label: 'General',
key: 'general',
children: <GeneralDashboardSettings />,
},
{ label: 'Variables', key: 'variables', children: <VariablesSetting /> },
];
return <Tabs items={items} />;
}

View File

@ -34,6 +34,7 @@ function NewExplorerCTA(): JSX.Element | null {
icon={<CompassOutlined />}
onClick={onClickHandler}
danger
data-testid="newExplorerCTA"
type="primary"
>
{buttonText[location.pathname]}

View File

@ -4,8 +4,8 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import useUrlQuery from 'hooks/useUrlQuery';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartData';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getChartData';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { useCallback, useMemo, useRef } from 'react';
import { UseQueryResult } from 'react-query';
import { useDispatch } from 'react-redux';

View File

@ -261,7 +261,12 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
)}
{!isSaveDisabled && (
<Button type="primary" disabled={isSaveDisabled} onClick={onSaveDashboard}>
<Button
type="primary"
data-testid="new-widget-save"
disabled={isSaveDisabled}
onClick={onSaveDashboard}
>
Save
</Button>
)}

View File

@ -1,144 +0,0 @@
.apm-module-container {
padding: 48px 0;
.header {
h1 {
font-size: 24px;
font-weight: 500;
}
h4 {
font-size: 14px;
font-weight: 300;
}
}
}
.supported-languages-container {
display: flex;
gap: 24px;
}
.supported-language {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 120px;
background: #1d1d1d;
border: 1px solid #424242;
border-radius: 3px;
color: #424242;
cursor: pointer;
&.selected {
background-color: #111a2c;
border: 0.5px solid #3c89e8;
}
}
.supported-langauge-img {
height: 48px;
}
.selected-langauage-setup-instructions {
padding: 24px 0;
}
div[class*='-setup-instructions-container'] {
.header {
display: flex;
align-items: center;
margin: 16px 0;
img {
height: 40px;
}
h1 {
font-size: 18px;
display: flex;
align-items: center;
color: #e5e7eb;
gap: 16px;
margin: 12px;
}
}
}
.label {
font-size: 14px;
margin-bottom: 8px;
font-weight: 300;
}
pre {
background-color: #292d3e;
padding: 8px;
overflow: auto;
border-radius: 3px;
code {
overflow: auto;
text-wrap: wrap;
}
}
.content-container {
padding: 24px;
margin: 16px 0;
background: rgba(29, 29, 29, 1);
line-height: 20px;
}
.detailed-docs-link {
display: flex;
margin: 12px;
font-size: 12px;
a {
padding-left: 4px;
}
}
.form-container {
display: flex;
align-items: flex-start;
width: 100%;
gap: 16px;
& .ant-form-item {
margin-bottom: 0px;
}
}
$lightModeFontColor: rgb(29, 29, 29);
.lightMode {
.apm-module-container {
.header {
color: $lightModeFontColor;
}
div[class*='-setup-instructions-container'] {
.header {
h1 {
color: $lightModeFontColor;
}
}
.framework-selector {
.label {
color: $lightModeFontColor;
}
}
.service-name-container {
.label {
color: $lightModeFontColor;
}
}
}
}
}

View File

@ -1,147 +0,0 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import './APM.styles.scss';
import getIngestionData from 'api/settings/getIngestionData';
import cx from 'classnames';
import { useEffect, useState } from 'react';
import { useQuery } from 'react-query';
import { trackEvent } from 'utils/segmentAnalytics';
import GoLang from './GoLang/GoLang';
import Java from './Java/Java';
import Javascript from './Javascript/Javascript';
import Python from './Python/Python';
import RoR from './RubyOnRails/ROR';
interface IngestionInfoProps {
SIGNOZ_INGESTION_KEY?: string;
REGION?: string;
}
export interface LangProps {
ingestionInfo: IngestionInfoProps;
activeStep: number;
}
const supportedLanguages = [
{
name: 'java',
imgURL: `Logos/java.png`,
},
{
name: 'python',
imgURL: `Logos/java.png`,
},
{
name: 'javascript',
imgURL: `Logos/java.png`,
},
{
name: 'go',
imgURL: `Logos/java.png`,
},
{
name: 'rails',
imgURL: `Logos/rails.png`,
},
];
export default function APM({
activeStep,
}: {
activeStep: number;
}): JSX.Element {
const [selectedLanguage, setSelectedLanguage] = useState('java');
const [ingestionInfo, setIngestionInfo] = useState<IngestionInfoProps>({});
const { status, data: ingestionData } = useQuery({
queryFn: () => getIngestionData(),
});
useEffect(() => {
if (
status === 'success' &&
ingestionData.payload &&
Array.isArray(ingestionData.payload)
) {
const payload = ingestionData.payload[0] || {
ingestionKey: '',
dataRegion: '',
};
setIngestionInfo({
SIGNOZ_INGESTION_KEY: payload?.ingestionKey,
REGION: payload?.dataRegion,
});
}
}, [status, ingestionData?.payload]);
useEffect(() => {
// on language select
trackEvent('Onboarding: APM', {
selectedLanguage,
activeStep,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedLanguage]);
const renderSelectedLanguageSetupInstructions = (): JSX.Element => {
switch (selectedLanguage) {
case 'java':
return <Java ingestionInfo={ingestionInfo} activeStep={activeStep} />;
case 'python':
return <Python ingestionInfo={ingestionInfo} activeStep={activeStep} />;
case 'javascript':
return <Javascript ingestionInfo={ingestionInfo} activeStep={activeStep} />;
case 'go':
return <GoLang ingestionInfo={ingestionInfo} activeStep={activeStep} />;
case 'rails':
return <RoR ingestionInfo={ingestionInfo} activeStep={activeStep} />;
default:
return <> </>;
}
};
return (
<div className="apm-module-container">
{activeStep === 2 && (
<>
<div className="header">
<h1>
Get Started to instrument your applications and sending data to SigNoz
</h1>
<h4> Select the data source </h4>
</div>
<div className="supported-languages-container">
{supportedLanguages.map((supportedLanguage) => (
<div
className={cx(
'supported-language',
selectedLanguage === supportedLanguage.name ? 'selected' : '',
)}
key={supportedLanguage.name}
onClick={(): void => setSelectedLanguage(supportedLanguage.name)}
>
<img
className={cx('supported-langauge-img')}
src={`/Logos/${supportedLanguage.name}.png`}
alt=""
/>
</div>
))}
</div>
</>
)}
{selectedLanguage && (
<div
className={cx('selected-langauage-setup-instructions', selectedLanguage)}
>
{renderSelectedLanguageSetupInstructions()}
</div>
)}
</div>
);
}

View File

@ -1,68 +0,0 @@
import './GoLang.styles.scss';
import { Form, Input } from 'antd';
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import { LangProps } from '../APM';
import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus';
import GoLangDocs from './goLang.md';
export default function GoLang({
ingestionInfo,
activeStep,
}: LangProps): JSX.Element {
const [form] = Form.useForm();
const serviceName = Form.useWatch('Service Name', form);
const variables = {
MYAPP: serviceName || '<service-name>',
SIGNOZ_INGESTION_KEY:
ingestionInfo.SIGNOZ_INGESTION_KEY || '<SIGNOZ_INGESTION_KEY>',
REGION: ingestionInfo.REGION || 'region',
};
return (
<>
{activeStep === 2 && (
<div className="golang-setup-instructions-container">
<Header
entity="go"
heading="Go OpenTelemetry Instrumentation"
imgURL="/Logos/go.png"
docsURL="https://signoz.io/docs/instrumentation/golang/"
imgClassName="supported-language-img"
/>
<div className="form-container">
<div className="service-name-container">
<div className="label"> Service Name </div>
<Form form={form} name="service-name" style={{ minWidth: '300px' }}>
<Form.Item
hasFeedback
name="Service Name"
rules={[{ required: true }]}
validateTrigger="onBlur"
>
<Input autoFocus />
</Form.Item>
</Form>
</div>
</div>
<div className="content-container">
<MarkdownRenderer markdownContent={GoLangDocs} variables={variables} />
</div>
</div>
)}
{activeStep === 3 && (
<ConnectionStatus
serviceName={form.getFieldValue('Service Name')}
framework="go"
language="go"
/>
)}
</>
);
}

View File

@ -1,10 +0,0 @@
.form-container {
display: flex;
align-items: flex-start;
width: 100%;
gap: 16px;
& .ant-form-item {
margin-bottom: 0px;
}
}

View File

@ -1,146 +0,0 @@
import './Java.styles.scss';
import { Form, Input, Select } from 'antd';
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import { useEffect, useState } from 'react';
import { trackEvent } from 'utils/segmentAnalytics';
import { popupContainer } from 'utils/selectPopupContainer';
import { LangProps } from '../APM';
import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus';
import JavaDocs from './md-docs/java.md';
import JbossDocs from './md-docs/jboss.md';
import SprintBootDocs from './md-docs/spring_boot.md';
import TomcatDocs from './md-docs/tomcat.md';
enum FrameworksMap {
tomcat = 'Tomcat',
spring_boot = 'Spring Boot',
jboss = 'JBoss',
other = 'Others',
}
export default function Java({
ingestionInfo,
activeStep,
}: LangProps): JSX.Element {
const [selectedFrameWork, setSelectedFrameWork] = useState('spring_boot');
const [selectedFrameWorkDocs, setSelectedFrameWorkDocs] = useState(
SprintBootDocs,
);
const [form] = Form.useForm();
const serviceName = Form.useWatch('Service Name', form);
useEffect(() => {
// on language select
trackEvent('Onboarding: APM : Java', {
selectedFrameWork,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedFrameWork]);
const handleFrameworkChange = (selectedFrameWork: string): void => {
setSelectedFrameWork(selectedFrameWork);
switch (selectedFrameWork) {
case 'tomcat':
setSelectedFrameWorkDocs(TomcatDocs);
break;
case 'spring_boot':
setSelectedFrameWorkDocs(SprintBootDocs);
break;
case 'jboss':
setSelectedFrameWorkDocs(JbossDocs);
break;
default:
setSelectedFrameWorkDocs(JavaDocs);
break;
}
};
const variables = {
MYAPP: serviceName || '<service-name>',
SIGNOZ_INGESTION_KEY:
ingestionInfo.SIGNOZ_INGESTION_KEY || '<SIGNOZ_INGESTION_KEY>',
REGION: ingestionInfo.REGION || 'region',
};
return (
<>
{activeStep === 2 && (
<div className="java-setup-instructions-container">
<Header
entity="java"
heading="Java OpenTelemetry Instrumentation"
imgURL="/Logos/java.png"
docsURL="https://signoz.io/docs/instrumentation/java/"
imgClassName="supported-language-img"
/>
<div className="form-container">
<div className="framework-selector">
<div className="label"> Select Framework </div>
<Select
getPopupContainer={popupContainer}
defaultValue="spring_boot"
style={{ minWidth: 120 }}
placeholder="Select Framework"
onChange={(value): void => handleFrameworkChange(value)}
options={[
{
value: 'spring_boot',
label: FrameworksMap.spring_boot,
},
{
value: 'tomcat',
label: FrameworksMap.tomcat,
},
{
value: 'jboss',
label: FrameworksMap.jboss,
},
{
value: 'other',
label: FrameworksMap.other,
},
]}
/>
</div>
<div className="service-name-container">
<div className="label"> Service Name </div>
<Form form={form} name="service-name" style={{ minWidth: '300px' }}>
<Form.Item
hasFeedback
name="Service Name"
rules={[{ required: true }]}
validateTrigger="onBlur"
>
<Input autoFocus />
</Form.Item>
</Form>
</div>
</div>
<div className="content-container">
<MarkdownRenderer
markdownContent={selectedFrameWorkDocs}
variables={variables}
/>
</div>
</div>
)}
{activeStep === 3 && (
<ConnectionStatus
serviceName={form.getFieldValue('Service Name')}
language="java"
framework={(FrameworksMap as any)[selectedFrameWork]}
/>
)}
</>
);
}

View File

@ -1,143 +0,0 @@
import './Javascript.styles.scss';
import { Form, Input, Select } from 'antd';
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import { useEffect, useState } from 'react';
import { trackEvent } from 'utils/segmentAnalytics';
import { popupContainer } from 'utils/selectPopupContainer';
import { LangProps } from '../APM';
import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus';
import ExpressDocs from './md-docs/express.md';
import JavascriptDocs from './md-docs/javascript.md';
import NestJsDocs from './md-docs/nestjs.md';
const frameworksMap = {
express: 'Express',
nestjs: 'Nest JS',
nodejs: 'Nodejs',
};
export default function Javascript({
ingestionInfo,
activeStep,
}: LangProps): JSX.Element {
const [selectedFrameWork, setSelectedFrameWork] = useState('express');
const [selectedFrameWorkDocs, setSelectedFrameWorkDocs] = useState(
ExpressDocs,
);
const [form] = Form.useForm();
const serviceName = Form.useWatch('Service Name', form);
const variables = {
MYAPP: serviceName || '<service-name>',
SIGNOZ_INGESTION_KEY:
ingestionInfo.SIGNOZ_INGESTION_KEY || '<SIGNOZ_INGESTION_KEY>',
REGION: ingestionInfo.REGION || 'region',
};
useEffect(() => {
// on language select
trackEvent('Onboarding: APM : Javascript', {
selectedFrameWork,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedFrameWork]);
const handleFrameworkChange = (selectedFrameWork: string): void => {
setSelectedFrameWork(selectedFrameWork);
switch (selectedFrameWork) {
case 'nodejs':
setSelectedFrameWorkDocs(JavascriptDocs);
break;
case 'nestjs':
setSelectedFrameWorkDocs(NestJsDocs);
break;
default:
setSelectedFrameWorkDocs(ExpressDocs);
break;
}
};
return (
<>
{activeStep === 2 && (
<div className="javascript-setup-instructions-container">
<Header
entity="javascript"
heading="Javascript OpenTelemetry Instrumentation"
imgURL="/Logos/javascript.png"
docsURL="https://signoz.io/docs/instrumentation/javascript/"
imgClassName="supported-language-img"
/>
<div className="form-container">
<div className="framework-selector">
<div className="label"> Select Framework </div>
<Select
getPopupContainer={popupContainer}
defaultValue="express"
style={{ minWidth: 120 }}
placeholder="Select Framework"
onChange={(value): void => handleFrameworkChange(value)}
options={[
{
value: 'nodejs',
label: frameworksMap.nodejs,
},
{
value: 'express',
label: frameworksMap.express,
},
{
value: 'nestjs',
label: frameworksMap.nestjs,
},
]}
/>
</div>
<div className="service-name-container">
<div className="label"> Service Name </div>
<Form
form={form}
name="service-name"
style={{ minWidth: '300px' }}
scrollToFirstError
requiredMark
>
<Form.Item
hasFeedback
name="Service Name"
rules={[{ required: true }]}
validateTrigger="onBlur"
required
>
<Input autoFocus />
</Form.Item>
</Form>
</div>
</div>
<div className="content-container">
<MarkdownRenderer
markdownContent={selectedFrameWorkDocs}
variables={variables}
/>
</div>
</div>
)}
{activeStep === 3 && (
<ConnectionStatus
serviceName={form.getFieldValue('Service Name')}
language="javascript"
framework={selectedFrameWork}
/>
)}
</>
);
}

View File

@ -1,152 +0,0 @@
import './Python.styles.scss';
import { Form, Input, Select } from 'antd';
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import { useEffect, useState } from 'react';
import { trackEvent } from 'utils/segmentAnalytics';
import { popupContainer } from 'utils/selectPopupContainer';
import { LangProps } from '../APM';
import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus';
import DjangoDocs from './md-docs/django.md';
import FalconDocs from './md-docs/falcon.md';
import FastAPIDocs from './md-docs/fastAPI.md';
import FlaskDocs from './md-docs/flask.md';
import PythonDocs from './md-docs/python.md';
const frameworksMap = {
django: 'Django',
fastAPI: 'Fast API',
flask: 'Flask',
falcon: 'Falcon',
other: 'Others',
};
export default function Python({
ingestionInfo,
activeStep,
}: LangProps): JSX.Element {
const [selectedFrameWork, setSelectedFrameWork] = useState('django');
const [selectedFrameWorkDocs, setSelectedFrameWorkDocs] = useState(DjangoDocs);
const [form] = Form.useForm();
const serviceName = Form.useWatch('Service Name', form);
const variables = {
MYAPP: serviceName || '<service-name>',
SIGNOZ_INGESTION_KEY:
ingestionInfo.SIGNOZ_INGESTION_KEY || '<SIGNOZ_INGESTION_KEY>',
REGION: ingestionInfo.REGION || 'region',
};
useEffect(() => {
// on language select
trackEvent('Onboarding: APM : Python', {
selectedFrameWork,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedFrameWork]);
const handleFrameworkChange = (selectedFrameWork: string): void => {
setSelectedFrameWork(selectedFrameWork);
switch (selectedFrameWork) {
case 'django':
setSelectedFrameWorkDocs(DjangoDocs);
break;
case 'fastAPI':
setSelectedFrameWorkDocs(FastAPIDocs);
break;
case 'flask':
setSelectedFrameWorkDocs(FlaskDocs);
break;
case 'falcon':
setSelectedFrameWorkDocs(FalconDocs);
break;
default:
setSelectedFrameWorkDocs(PythonDocs);
break;
}
};
return (
<>
{activeStep === 2 && (
<div className="python-setup-instructions-container">
<Header
entity="python"
heading="Python OpenTelemetry Instrumentation"
imgURL="/Logos/python.png"
docsURL="https://signoz.io/docs/instrumentation/python/"
imgClassName="supported-language-img"
/>
<div className="form-container">
<div className="framework-selector">
<div className="label"> Select Framework </div>
<Select
getPopupContainer={popupContainer}
defaultValue="Django"
style={{ minWidth: 120 }}
placeholder="Select Framework"
onChange={(value): void => handleFrameworkChange(value)}
options={[
{
value: 'django',
label: frameworksMap.django,
},
{
value: 'fastAPI',
label: frameworksMap.fastAPI,
},
{
value: 'flask',
label: frameworksMap.flask,
},
{
value: 'falcon',
label: frameworksMap.falcon,
},
{
value: 'other',
label: frameworksMap.other,
},
]}
/>
</div>
<div className="service-name-container">
<div className="label"> Service Name </div>
<Form form={form} name="service-name" style={{ minWidth: '300px' }}>
<Form.Item
hasFeedback
name="Service Name"
rules={[{ required: true }]}
validateTrigger="onBlur"
>
<Input autoFocus />
</Form.Item>
</Form>
</div>
</div>
<div className="content-container">
<MarkdownRenderer
markdownContent={selectedFrameWorkDocs}
variables={variables}
/>
</div>
</div>
)}
{activeStep === 3 && (
<ConnectionStatus
serviceName={form.getFieldValue('Service Name')}
language="python"
framework={selectedFrameWork}
/>
)}
</>
);
}

View File

@ -1,68 +0,0 @@
import './ROR.styles.scss';
import { Form, Input } from 'antd';
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import { LangProps } from '../APM';
import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus';
import RORDocs from './RubyOnRails.md';
export default function RoR({
ingestionInfo,
activeStep,
}: LangProps): JSX.Element {
const [form] = Form.useForm();
const serviceName = Form.useWatch('Service Name', form);
const variables = {
MYAPP: serviceName || '<service-name>',
SIGNOZ_INGESTION_KEY:
ingestionInfo.SIGNOZ_INGESTION_KEY || '<SIGNOZ_INGESTION_KEY>',
REGION: ingestionInfo.REGION || 'region',
};
return (
<>
{activeStep === 2 && (
<div className="ror-setup-instructions-container">
<Header
entity="rails"
heading="Ruby on Rails OpenTelemetry Instrumentation"
imgURL="/Logos/rails.png"
docsURL="https://signoz.io/docs/instrumentation/ruby-on-rails/"
imgClassName="supported-language-img"
/>
<div className="form-container">
<div className="service-name-container">
<div className="label"> Service Name </div>
<Form form={form} name="service-name" style={{ minWidth: '300px' }}>
<Form.Item
hasFeedback
name="Service Name"
rules={[{ required: true }]}
validateTrigger="onBlur"
>
<Input autoFocus />
</Form.Item>
</Form>
</div>
</div>
<div className="content-container">
<MarkdownRenderer markdownContent={RORDocs} variables={variables} />
</div>
</div>
)}
{activeStep === 3 && (
<ConnectionStatus
serviceName={form.getFieldValue('Service Name')}
framework="rails"
language="rails"
/>
)}
</>
);
}

View File

@ -1,45 +0,0 @@
.infrastructure-monitoring-module-container {
padding: 48px 0;
.module-header {
h1 {
font-size: 24px;
font-weight: 500;
}
h4 {
font-size: 14px;
font-weight: 300;
}
}
.content-container {
.heading {
.title {
a {
color: rgb(232, 112, 64);
}
}
}
.subheading {
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 16px;
.recevier-types {
display: flex;
flex-direction: column;
}
}
}
.header {
display: flex;
gap: 16px;
justify-content: flex-start;
align-items: center;
margin: 16px 0;
}
}

View File

@ -1,154 +0,0 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import './InfrastructureMonitoring.styles.scss';
import cx from 'classnames';
import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer';
import { useEffect, useState } from 'react';
import ReactMarkdown from 'react-markdown';
import { trackEvent } from 'utils/segmentAnalytics';
import Header from '../common/Header/Header';
import hostMetricsMonitoring from './md-docs/hostMetricsMonitoring.md';
import k8sInfraMonitoringDocs from './md-docs/kubernetesInfraMonitoring.md';
import otherMetrics from './md-docs/otherMetrics.md';
export default function InfrastructureMonitoring({
activeStep,
}: {
activeStep: number;
}): JSX.Element {
const [selectedInfraMetrics, setSelectedInfraMetrics] = useState('kubernetes');
const [selectedInfraMetricsDocs, setSelectedInfraMetricsDocs] = useState(
k8sInfraMonitoringDocs,
);
useEffect(() => {
// on metrics Type select
trackEvent('Onboarding: APM : Java', {
selectedInfraMetrics,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedInfraMetrics]);
const supportedInfraMetrics = [
{
name: 'Kubernetes Infra Metrics',
id: 'kubernetes',
imgURL: `Logos/kubernetes.svg`,
},
{
name: 'HostMetrics',
id: 'hostMetrics',
imgURL: `Logos/software-window.svg`,
},
{
name: 'Other Metrics',
id: 'otherMetrics',
imgURL: `Logos/cmd-terminal.svg`,
},
];
const handleMetricsTypeChange = (selectedMetricsType: string): void => {
setSelectedInfraMetrics(selectedMetricsType);
switch (selectedMetricsType) {
case 'kubernetes':
setSelectedInfraMetricsDocs(k8sInfraMonitoringDocs);
break;
case 'hostMetrics':
setSelectedInfraMetricsDocs(hostMetricsMonitoring);
break;
case 'otherMetrics':
setSelectedInfraMetricsDocs(otherMetrics);
break;
default:
setSelectedInfraMetricsDocs(otherMetrics);
break;
}
};
const getHeaderBasedOnType = (): JSX.Element => {
switch (selectedInfraMetrics) {
case 'hostMetrics':
return (
<Header
entity="hostMetrics"
heading="Host Metrics"
imgURL="/Logos/software-window.svg"
docsURL="https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/"
imgClassName="supported-logs-type-img"
/>
);
case 'otherMetrics':
return (
<Header
entity="otherMetrics"
heading="Other Metrics"
imgURL="/Logos/cmd-terminal.svg"
docsURL="https://signoz.io/docs/userguide/send-metrics-cloud/"
imgClassName="supported-logs-type-img"
/>
);
default:
return (
<Header
entity="kubernetes"
heading="Kubernetes Metrics"
imgURL="/Logos/kubernetes.svg"
docsURL="https://signoz.io/docs/tutorial/kubernetes-infra-metrics/"
imgClassName="supported-logs-type-img"
/>
);
}
};
return (
<div className="infrastructure-monitoring-module-container">
{activeStep === 2 && (
<>
<div className="module-header">
<h1>Select an Infra Metrics type</h1>
{/* <h4> Choose the logs that you want to receive on SigNoz </h4> */}
</div>
<div className="supported-logs-type-container">
{supportedInfraMetrics.map((logType) => (
<div
className={cx(
'supported-logs-type',
selectedInfraMetrics === logType.id ? 'selected' : '',
)}
key={logType.name}
onClick={(): void => handleMetricsTypeChange(logType.id)}
>
<img
className={cx('supported-logs-type-img')}
src={`${logType.imgURL}`}
alt=""
/>
<div> {logType.name} </div>
</div>
))}
</div>
{getHeaderBasedOnType()}
<div className="content-container">
<ReactMarkdown
components={{
pre: Pre,
code: Code,
}}
>
{selectedInfraMetricsDocs}
</ReactMarkdown>
</div>
</>
)}
</div>
);
}

View File

@ -1,10 +0,0 @@
## Hostmetrics Monitoring
You can collect Hostmetrics from your VM and send it to SigNoz cloud using OpenTelemetry Collector.
Steps to send hostmetrics to SigNoz Cloud:
- Install OpenTelemetry Collector binary agent. Please find instructions [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/#setup-otel-collector-as-agent).
- Import Hostmetrics Dashboard in SigNoz. Please find instructions [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/#hostmetrics-dashboard).
Learn how to create dashboards and panels [here](https://signoz.io/docs/userguide/manage-dashboards-and-panels/).

View File

@ -1,9 +0,0 @@
## Kubernetes Infra Metrics
You can collect Kubernetes infra metrics from your k8s cluster and send it to SigNoz cloud using k8s-infra chart.
Steps to send kubernetes infra metrics to SigNoz Cloud:
- Install OpenTelemetry Collectors in your k8s infra. Please find instructions [here](https://signoz.io/docs/tutorial/kubernetes-infra-metrics/).
- Plot metrics in SigNoz UI by following the instructions [here](https://signoz.io/docs/tutorial/kubernetes-infra-metrics/#plot-metrics-in-signoz-ui).

View File

@ -1,195 +0,0 @@
## Send metrics from any third-party integrations
This document helps you to send metrics from any third-party integrations such as RabbitMQ, Nginx, MySQL, etc.
There are two ways in which you can send metrics to SigNoz using OpenTelemetry:
- From your application
- From OpenTelemetry Collector
In this document, we will cover how to send metrics from OpenTelemetry Collector. The Collector is a swiss-army knife that can collect metrics from various sources and send them to SigNoz.
- Enable a Specific Metric Receiver
- Enable a Prometheus Receiver
## Enable a Specific Metric Receiver
SigNoz supports all the receivers that are listed in the [opentelemetry-collector-contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver) GitHub repository. To configure a new metric receiver, you must edit the `receivers` and `service::pipelines` sections of the `otel-collector-config.yaml` file. The following example shows the default configuration in which the `hostmetrics` receiver is enabled:
```yaml {8-20,52}
receivers:
otlp:
protocols:
grpc:
endpoint: localhost:4317
http:
endpoint: localhost:4318
hostmetrics:
collection_interval: 30s
scrapers:
cpu: {}
disk: {}
load: {}
filesystem: {}
memory: {}
network: {}
paging: {}
process:
mute_process_name_error: true
processes: {}
processors:
batch:
send_batch_size: 1000
timeout: 10s
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
resourcedetection:
detectors: [env, system, ec2] # include ec2 for AWS, gce for GCP and azure for Azure.
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
timeout: 2s
override: false
system:
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
exporters:
otlp:
endpoint: 'ingest.{region}.signoz.cloud:443' # replace {region} with your region
tls:
insecure: false
headers:
'signoz-access-token': '<SIGNOZ_INGESTION_KEY>'
logging:
loglevel: debug
service:
telemetry:
metrics:
address: localhost:8888
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
metrics/hostmetrics:
receivers: [hostmetrics]
processors: [resourcedetection, batch]
exporters: [otlp]
```
Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary according to this table.
US - ingest.us.signoz.cloud:443
IN - ingest.in.signoz.cloud:443
EU - ingest.eu.signoz.cloud:443
To enable a new OpenTelemetry receiver, follow the steps below:
1. Open the `otel-collector-config.yaml` file in a plain-text editor.
2. Configure your receivers. The following example shows how you can enable a `rabbitmq` receiver:
```yaml {21-25,53}
receivers:
otlp:
protocols:
grpc:
endpoint: localhost:4317
http:
endpoint: localhost:4318
hostmetrics:
collection_interval: 30s
scrapers:
cpu: {}
disk: {}
load: {}
filesystem: {}
memory: {}
network: {}
paging: {}
process:
mute_process_name_error: true
processes: {}
rabbitmq:
endpoint: http://localhost:15672
username: <RABBITMQ_USERNAME>
password: <RABBITMQ_PASSWORD>
collection_interval: 10s
processors:
batch:
send_batch_size: 1000
timeout: 10s
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
resourcedetection:
detectors: [env, system, ec2] # include ec2 for AWS, gce for GCP and azure for Azure.
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
timeout: 2s
override: false
system:
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
exporters:
otlp:
endpoint: 'ingest.{region}.signoz.cloud:443' # replace {region} with your region
tls:
insecure: false
headers:
'signoz-access-token': '<SIGNOZ_INGESTION_KEY>'
logging:
loglevel: debug
service:
telemetry:
metrics:
address: localhost:8888
pipelines:
metrics:
receivers: [otlp, rabbitmq]
processors: [batch]
exporters: [otlp]
metrics/hostmetrics:
receivers: [hostmetrics]
processors: [resourcedetection, batch]
exporters: [otlp]
```
For details about configuring OpenTelemetry receivers, see the [README](https://github.com/open-telemetry/opentelemetry-collector/blob/main/receiver/README.md) page of the `opentelemetry-collector` GitHub repository.
## Enable a Prometheus Receiver
SigNoz supports all the exporters that are listed on the [Exporters and Integrations](https://prometheus.io/docs/instrumenting/exporters/) page of the Prometheus documentation. If you have a running Prometheus instance, and you expose metrics in Prometheus, then you can scrape them in SigNoz by configuring Prometheus receivers in the `receivers::prometheus::config::scrape_configs` section of the `otel-collector-config.yaml` file.
To enable a Prometheus receiver, follow the steps below:
1. Open the `otel-collector-config.yaml` file in a plain-text editor.
2. Enable a new Prometheus receiver. Depending on your use case, there are two ways in which you can enable a new Prometheus exporter:
- **By creating a new job**: The following example shows how you can enable a Prometheus receiver by creating a new job named `my-new-job`:
```yaml {10-13}
...
# Data sources: metrics
prometheus:
config:
scrape_configs:
- job_name: "otel-collector"
scrape_interval: 30s
static_configs:
- targets: ["otel-collector:8889"]
- job_name: "my-new-job"
scrape_interval: 30s
static_configs:
- targets: ["localhost:8080"]
...
# This file was truncated for brevity.
```
- **By adding a new target to an existing job**: The following example shows the default `otel-collector` job to which a new target (`localhost:8080`) was added:
```yaml {9}
...
# Data sources: metrics
prometheus:
config:
scrape_configs:
- job_name: "otel-collector"
scrape_interval: 30s
static_configs:
- targets: ["otel-collector:8889", "localhost:8080"]
...
# This file was truncated for brevity.
```
Note that all the jobs are scraped in parallel, and all targets inside a job are scraped serially. For more details about configuring jobs and targets, see the following sections of the Prometheus documentation:
- [<Scrape_config>](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config)
- [Jobs and Instances](https://prometheus.io/docs/concepts/jobs_instances/)

View File

@ -1,53 +0,0 @@
import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import ReactMarkdown from 'react-markdown';
import ConnectionStatus from '../common/LogsConnectionStatus/LogsConnectionStatus';
import LogsFromLogFile from './applicationLogsFromLogFile.md';
interface ApplicationLogsProps {
type: string;
activeStep: number;
}
const collectLogsFromFileURL =
'https://signoz.io/docs/userguide/collect_logs_from_file/';
export default function ApplicationLogs({
type,
activeStep,
}: ApplicationLogsProps): JSX.Element {
const docsURL = collectLogsFromFileURL;
return (
<>
{activeStep === 2 && (
<div className="golang-setup-instructions-container">
<Header
entity="docker"
heading="Collecting Application Logs from Log file"
imgURL={`/Logos/${'software-window'}.svg`}
docsURL={docsURL}
imgClassName="supported-logs-type-img"
/>
<div className="content-container">
<ReactMarkdown
components={{
pre: Pre,
code: Code,
}}
>
{LogsFromLogFile}
</ReactMarkdown>
</div>
</div>
)}
{activeStep === 3 && (
<div className="connection-status-container">
<ConnectionStatus logType={type} />
</div>
)}
</>
);
}

View File

@ -1,44 +0,0 @@
import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import ReactMarkdown from 'react-markdown';
import ConnectionStatus from '../common/LogsConnectionStatus/LogsConnectionStatus';
import DockerDocs from './docker.md';
export default function Docker({
activeStep,
}: {
activeStep: number;
}): JSX.Element {
return (
<>
{activeStep === 2 && (
<div className="golang-setup-instructions-container">
<Header
entity="docker"
heading="Collecting Docker container logs"
imgURL="/Logos/docker.svg"
docsURL="https://signoz.io/docs/userguide/collect_docker_logs/"
imgClassName="supported-logs-type-img"
/>
<div className="content-container">
<ReactMarkdown
components={{
pre: Pre,
code: Code,
}}
>
{DockerDocs}
</ReactMarkdown>
</div>
</div>
)}
{activeStep === 3 && (
<div className="connection-status-container">
<ConnectionStatus logType="docker" />
</div>
)}
</>
);
}

View File

@ -1,97 +0,0 @@
import { Select } from 'antd';
import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import { useEffect, useState } from 'react';
import ReactMarkdown from 'react-markdown';
import { trackEvent } from 'utils/segmentAnalytics';
import { popupContainer } from 'utils/selectPopupContainer';
import FluentBit from './md-docs/fluentBit.md';
import FluentD from './md-docs/fluentD.md';
import LogStashDocs from './md-docs/logStash.md';
enum FrameworksMap {
fluent_d = 'FluentD',
fluent_bit = 'FluentBit',
logstash = 'Logstash',
}
export default function ExistingCollectors(): JSX.Element {
const [selectedFrameWork, setSelectedFrameWork] = useState('fluent_d');
const [selectedFrameWorkDocs, setSelectedFrameWorkDocs] = useState(FluentD);
useEffect(() => {
// on language select
trackEvent('Onboarding: Logs Management: Existing Collectors', {
selectedFrameWork,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedFrameWork]);
const handleFrameworkChange = (selectedFrameWork: string): void => {
setSelectedFrameWork(selectedFrameWork);
switch (selectedFrameWork) {
case 'fluent_d':
setSelectedFrameWorkDocs(FluentD);
break;
case 'fluent_bit':
setSelectedFrameWorkDocs(FluentBit);
break;
default:
setSelectedFrameWorkDocs(LogStashDocs);
break;
}
};
return (
<div className="java-setup-instructions-container">
<Header
entity="existing_collectors"
heading="Logs from existing collectors"
imgURL="/Logos/cmd-terminal.svg"
docsURL="https://signoz.io/docs/userguide/fluentbit_to_signoz/"
imgClassName="supported-language-img"
/>
<div className="form-container">
<div className="framework-selector">
<div className="label"> Select Framework </div>
<Select
getPopupContainer={popupContainer}
defaultValue="fluent_d"
style={{ minWidth: 120 }}
placeholder="Select Framework"
onChange={(value): void => handleFrameworkChange(value)}
options={[
{
value: 'fluent_d',
label: FrameworksMap.fluent_d,
},
{
value: 'fluent_bit',
label: FrameworksMap.fluent_bit,
},
{
value: 'logstash',
label: FrameworksMap.logstash,
},
]}
/>
</div>
</div>
<div className="content-container">
<ReactMarkdown
components={{
pre: Pre,
code: Code,
}}
>
{selectedFrameWorkDocs}
</ReactMarkdown>
</div>
</div>
);
}

View File

@ -1,58 +0,0 @@
## Collect Logs Using FluentBit in SigNoz cloud
If you use fluentBit to collect logs in your stack, you will be able to send logs from fluentBit to SigNoz.
At SigNoz we use opentelemetry collector to recieve logs which supports the fluentforward protocol. So you can forward your logs from your fluentBit agent to opentelemetry collector using fluentforward protocol.
* Add otel collector binary to your VM by following this [guide](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/).
* Add fluentforward reciever to your `config.yaml`
```yaml
receivers:
fluentforward:
endpoint: 0.0.0.0:24224
```
Here we have used port 24224 for listening in fluentforward protocol, but you can change it to a port you want.
You can read more about fluentforward receiver [here](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/fluentforwardreceiver).
* Modify your `config.yaml` and add the above receiver
```yaml {4}
service:
....
logs:
receivers: [otlp, fluentforward]
processors: [batch]
exporters: [otlp]
```
* Add the following to your fluentBit config to forward the logs to otel collector.
```
[OUTPUT]
Name forward
Match *
Host localhost
Port 24224
```
In this config we are forwarding the logs to the otel collector which is listening on port 24224.
Also we are assuming that you are running the fluentBit binary on the host. If not, the value of `host` might change depending on your environment.
* Once you make this changes you can restart fluentBit and otel-binary, and you will be able to see the logs in SigNoz.
* To properly transform your existing log model into opentelemetry [log](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md) model you can use the different processors provided by opentelemetry. ([link](https://signoz.io/docs/userguide/logs/#processors-available-for-processing-logs))
eg:-
```yaml
processors:
logstransform:
operators:
- type: trace_parser
trace_id:
parse_from: attributes.trace_id
span_id:
parse_from: attributes.span_id
- type: remove
field: attributes.trace_id
- type: remove
field: attributes.span_id
```
The operations in the above processor will parse the trace_id and span_id from log to opentelemetry log model and remove them from attributes.

View File

@ -1,66 +0,0 @@
## Collect Logs Using FluentD in SigNoz cloud
If you use fluentD to collect logs in your stack, you will be able to send logs from fluentD to SigNoz.
At SigNoz, we use opentelemetry collector to recieve logs which supports the fluentforward protocol. So you can forward your logs from your fluentD agent to opentelemetry collector.
* Add otel collector binary to your VM by following this [guide](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/).
* Add fluentforward reciever to your `config.yaml`
```yaml
receivers:
fluentforward:
endpoint: 0.0.0.0:24224
```
Here we have used port 24224 for listening in fluentforward protocol, but you can change it to a port you want.
You can read more about fluentforward receiver [here](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/fluentforwardreceiver).
* Modify your `config.yaml` and add the above receiver
```yaml {4}
service:
....
logs:
receivers: [otlp, fluentforward]
processors: [batch]
exporters: [otlp]
```
* Add the following to your fluentD config to forward the logs to otel collector.
```
<match <directive>>
@type forward
send_timeout 60s
recover_wait 10s
hard_timeout 60s
<server>
name myserver1
host localhost
port 24224
</server>
</match>
```
In this config we are matching a directive and forwarding logs to the otel collector which is listening on port 24224. Replace `<directive>` with your directive name.
Also we are assuming that you are running the fluentD binary on the host. If not, the value of `host` might change depending on your environment.
* Once you make this changes you can restart fluentD and otel-binary, and you will be able to see the logs in SigNoz.
* To properly transform your existing log model into opentelemetry [log](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md) model you can use the different processors provided by opentelemetry. ([link](https://signoz.io/docs/userguide/logs/#processors-available-for-processing-logs))
eg:-
```yaml
processors:
logstransform:
operators:
- type: trace_parser
trace_id:
parse_from: attributes.trace_id
span_id:
parse_from: attributes.span_id
- type: remove
field: attributes.trace_id
- type: remove
field: attributes.span_id
```
The operations in the above processor will parse the trace_id and span_id from log to opentelemetry log model and remove them from attributes.

View File

@ -1,48 +0,0 @@
## Collect Logs Using Logstash in SigNoz cloud
If you use logstash to collect logs in your stack, you will be able to send logs from Logstash to SigNoz.
At SigNoz we use OpenTelemetry Collector to recieve logs which supports the TCP protocol. So you can forward your logs from the logstash agent to opentelemetry collector.
* Add otel collector binary to your VM by following this [guide](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/).
* Add the reciever to your `config.yaml`
```yaml
receivers:
tcplog/logstash:
max_log_size: 1MiB
listen_address: "0.0.0.0:2255"
attributes: {}
resource: {}
add_attributes: false
operators: []
```
Here we have used port 2255 for listening in TCP protocol, but you can change it to a port you want.
You can read more about tcplog reciver [here](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/tcplogreceiver).
* Modify your `config.yaml` and add the above receiver
```yaml {4}
service:
....
logs:
receivers: [otlp, tcplog/logstash]
processors: [batch]
exporters: [otlp]
```
* Change the logstash config to forward the logs to otel collector.
```
output {
tcp {
codec => json_lines # this is required otherwise it will send eveything in a single line
host => "localhost"
port => 2255
}
}
```
Here we are configuring logstash to send logs to otel-collector that we ran in the previous step, which is listening on port 2255.
Also we are assuming that you are running the logstash binary on the host. If not, the value of `host` might change depending on your environment.
* Once you make this changes you can otel binary and logstash, and you will be able to see the logs in SigNoz.
* To properly transform your existing log model into opentelemetry [log](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md) model you can use the different processors provided by opentelemetry ([link](https://signoz.io/docs/userguide/logs/#processors-available-for-processing-logs)).

View File

@ -1,44 +0,0 @@
import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import ReactMarkdown from 'react-markdown';
import ConnectionStatus from '../common/LogsConnectionStatus/LogsConnectionStatus';
import KubernetesDocs from './kubernetes.md';
export default function Kubernetes({
activeStep,
}: {
activeStep: number;
}): JSX.Element {
return (
<>
{activeStep === 2 && (
<div className="golang-setup-instructions-container">
<Header
entity="kubernetes"
heading="Collecting Kubernetes Pod logs"
imgURL="/Logos/kubernetes.svg"
docsURL="https://signoz.io/docs/userguide/collect_kubernetes_pod_logs/#collect-kubernetes-pod-logs-in-signoz-cloud"
imgClassName="supported-logs-type-img"
/>
<div className="content-container">
<ReactMarkdown
components={{
pre: Pre,
code: Code,
}}
>
{KubernetesDocs}
</ReactMarkdown>
</div>
</div>
)}
{activeStep === 3 && (
<div className="connection-status-container">
<ConnectionStatus logType="kubernetes" />
</div>
)}
</>
);
}

View File

@ -1,152 +0,0 @@
.logs-management-module-container {
min-height: calc(100vh - 300px);
padding: 48px 0;
.header {
h1 {
font-size: 24px;
font-weight: 500;
}
h4 {
font-size: 14px;
font-weight: 300;
}
}
}
.supported-logs-type-container {
display: flex;
flex-wrap: wrap;
gap: 24px;
}
.supported-logs-type {
display: flex;
justify-content: center;
align-items: flex-start;
flex-direction: column;
gap: 16px;
width: 300px;
padding: 16px;
box-sizing: border-box;
height: 120px;
background: #1d1d1d;
border: 1px solid #424242;
border-radius: 3px;
color: #e5e7eb;
cursor: pointer;
&.selected {
background-color: #111a2c;
border: 0.5px solid #3c89e8;
}
}
.supported-logs-type-img {
height: 36px;
}
.selected-logs-type-setup-instructions {
padding: 24px 0;
}
div[class*='-setup-instructions-container'] {
.header {
display: flex;
align-items: flex-start;
gap: 12px;
margin: 16px 0;
img {
height: 32px;
min-height: 32px;
min-width: 32px;
}
h1 {
font-size: 18px;
display: flex;
align-items: center;
color: #e5e7eb;
gap: 16px;
margin: 0;
}
}
}
.label {
font-size: 14px;
margin-bottom: 8px;
font-weight: 300;
}
pre {
background-color: #292d3e;
padding: 8px;
overflow: auto;
border-radius: 3px;
code {
overflow: auto;
text-wrap: wrap;
}
}
.content-container {
padding: 24px;
margin: 16px 0;
background: rgba(29, 29, 29, 1);
line-height: 20px;
}
.detailed-docs-link {
display: flex;
margin: 8px 0;
font-size: 12px;
a {
padding-left: 4px;
}
}
.language-tab-item {
display: flex;
justify-content: center;
align-items: center;
img {
height: 28px;
}
}
$lightModeFontColor: rgb(29, 29, 29);
.lightMode {
.logs-management-module-container {
.header {
color: $lightModeFontColor;
}
div[class*='-setup-instructions-container'] {
.header {
h1 {
color: $lightModeFontColor;
}
}
.framework-selector {
.label {
color: $lightModeFontColor;
}
}
.service-name-container {
.label {
color: $lightModeFontColor;
}
}
}
}
}

View File

@ -1,130 +0,0 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import './LogsManagement.styles.scss';
import cx from 'classnames';
import { useEffect, useState } from 'react';
import { trackEvent } from 'utils/segmentAnalytics';
import ApplicationLogs from './ApplicationLogs/ApplicationLogs';
import Docker from './Docker/Docker';
import ExistingCollectors from './ExistingCollectors/ExistingCollectors';
import Kubernetes from './Kubernetes/Kubernetes';
import Nodejs from './Nodejs/Nodejs';
import SysLogs from './SysLogs/SysLogs';
const supportedLogTypes = [
{
name: 'Kubernetes Pod Logs',
id: 'kubernetes',
imgURL: `Logos/kubernetes.svg`,
},
{
name: 'Docker Container Logs',
id: 'docker',
imgURL: `Logos/docker.svg`,
},
{
name: 'SysLogs',
id: 'syslogs',
imgURL: `Logos/syslogs.svg`,
},
{
name: 'Application Logs',
id: 'application_logs_log_file',
imgURL: `Logos/software-window.svg`,
},
{
name: 'Logs from existing collectors',
id: 'existing_collectors',
imgURL: `Logos/cmd-terminal.svg`,
},
];
export default function LogsManagement({
activeStep,
handleLogTypeSelect,
}: {
activeStep: number;
handleLogTypeSelect: (id: string) => any;
}): JSX.Element {
const [selectedLogsType, setSelectedLogsType] = useState('kubernetes');
useEffect(() => {
// on language select
trackEvent('Onboarding: Logs Management', {
selectedLogsType,
activeStep,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedLogsType]);
const renderSelectedLanguageSetupInstructions = ():
| JSX.Element
| undefined => {
switch (selectedLogsType) {
case 'kubernetes':
return <Kubernetes activeStep={activeStep} />;
case 'docker':
return <Docker activeStep={activeStep} />;
case 'application_logs_log_file':
return <ApplicationLogs type="from-log-file" activeStep={activeStep} />;
case 'application_logs_otel_sdk':
return <ApplicationLogs type="using-otel-sdk" activeStep={activeStep} />;
case 'syslogs':
return <SysLogs activeStep={activeStep} />;
case 'nodejs':
return <Nodejs activeStep={activeStep} />;
case 'existing_collectors':
return <ExistingCollectors />;
default:
return <> </>;
}
};
return (
<div className="logs-management-module-container">
{activeStep === 2 && (
<>
<div className="header">
<h1>Select a Logs type</h1>
<h4> Choose the logs that you want to receive on SigNoz </h4>
</div>
<div className="supported-logs-type-container">
{supportedLogTypes.map((logType) => (
<div
className={cx(
'supported-logs-type',
selectedLogsType === logType.id ? 'selected' : '',
)}
key={logType.name}
onClick={() => {
handleLogTypeSelect(logType.id);
setSelectedLogsType(logType.id);
}}
>
<img
className={cx('supported-logs-type-img')}
src={`${logType.imgURL}`}
alt=""
/>
<div> {logType.name} </div>
</div>
))}
</div>
</>
)}
{selectedLogsType && (
<div
className={cx('selected-logs-type-setup-instructions', selectedLogsType)}
>
{renderSelectedLanguageSetupInstructions()}
</div>
)}
</div>
);
}

View File

@ -1,44 +0,0 @@
import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import ReactMarkdown from 'react-markdown';
import ConnectionStatus from '../common/LogsConnectionStatus/LogsConnectionStatus';
import NodeJsDocs from './nodejs.md';
export default function Nodejs({
activeStep,
}: {
activeStep: number;
}): JSX.Element {
return (
<>
{activeStep === 2 && (
<div className="golang-setup-instructions-container">
<Header
entity="nodejs"
heading="Collecting NodeJS winston logs"
imgURL="/Logos/node-js.svg"
docsURL="https://signoz.io/docs/userguide/collecting_nodejs_winston_logs/"
imgClassName="supported-logs-type-img"
/>
<div className="content-container">
<ReactMarkdown
components={{
pre: Pre,
code: Code,
}}
>
{NodeJsDocs}
</ReactMarkdown>
</div>
</div>
)}
{activeStep === 3 && (
<div className="connection-status-container">
<ConnectionStatus logType="nodejs" />
</div>
)}
</>
);
}

View File

@ -1,44 +0,0 @@
import { Code, Pre } from 'components/MarkdownRenderer/MarkdownRenderer';
import Header from 'container/OnboardingContainer/common/Header/Header';
import ReactMarkdown from 'react-markdown';
import ConnectionStatus from '../common/LogsConnectionStatus/LogsConnectionStatus';
import SysLogsDocs from './syslogs.md';
export default function SysLogs({
activeStep,
}: {
activeStep: number;
}): JSX.Element {
return (
<>
{activeStep === 2 && (
<div className="golang-setup-instructions-container">
<Header
entity="syslogs"
heading="Collecting Syslogs"
imgURL="/Logos/syslogs.svg"
docsURL="https://signoz.io/docs/userguide/collecting_syslogs/"
imgClassName="supported-logs-type-img"
/>
<div className="content-container">
<ReactMarkdown
components={{
pre: Pre,
code: Code,
}}
>
{SysLogsDocs}
</ReactMarkdown>
</div>
</div>
)}
{activeStep === 3 && (
<div className="connection-status-container">
<ConnectionStatus logType="syslogs" />
</div>
)}
</>
);
}

View File

@ -0,0 +1,21 @@
### Install otel-collector in your Kubernetes infra
Add the SigNoz Helm Chart repository
```bash
helm repo add signoz https://charts.signoz.io
```
If the chart is already present, update the chart to the latest using:
```bash
helm repo update
```
Install the Kubernetes Infrastructure chart provided by SigNoz
```bash
helm install my-release signoz/k8s-infra \
--set otelCollectorEndpoint=ingest.{{REGION}}.signoz.cloud:443 \
--set otelInsecure=false \
--set signozApiKey={{SIGNOZ_INGESTION_KEY}} \
--set global.clusterName=<CLUSTER_NAME>
```
- Replace `<CLUSTER_NAME>` with the name of the Kubernetes cluster or a unique identifier of the cluster.

View File

@ -0,0 +1,116 @@
After setting up the Otel collector agent, follow the steps below to instrumnet your Go Application
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,5 @@
To run your Go Gin application, use the below command :
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=true OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 go run main.go
```

View File

@ -0,0 +1,115 @@
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,5 @@
To run your Go Gin application, use the below command :
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=false OTEL_EXPORTER_OTLP_HEADERS=signoz-access-token={{SIGNOZ_INGESTION_KEY}} OTEL_EXPORTER_OTLP_ENDPOINT=ingest.{{REGION}}.signoz.cloud:443 go run main.go
```

View File

@ -0,0 +1,93 @@
### Setup OpenTelemetry Binary as an agent
### Step 1: Download otel-collector tar.gz
```bash
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_linux_amd64.tar.gz
```
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
```bash
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_linux_amd64.tar.gz -C otelcol-contrib
```
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
```bash
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
hostmetrics:
collection_interval: 60s
scrapers:
cpu: {}
disk: {}
load: {}
filesystem: {}
memory: {}
network: {}
paging: {}
process:
mute_process_name_error: true
mute_process_exe_error: true
mute_process_io_error: true
processes: {}
prometheus:
config:
global:
scrape_interval: 60s
scrape_configs:
- job_name: otel-collector-binary
static_configs:
- targets:
# - localhost:8888
processors:
batch:
send_batch_size: 1000
timeout: 10s
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
resourcedetection:
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
timeout: 2s
system:
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
extensions:
health_check: {}
zpages: {}
exporters:
otlp:
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
tls:
insecure: false
headers:
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
logging:
verbosity: normal
service:
telemetry:
metrics:
address: 0.0.0.0:8888
extensions: [health_check, zpages]
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
metrics/internal:
receivers: [prometheus, hostmetrics]
processors: [resourcedetection, batch]
exporters: [otlp]
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
```

View File

@ -0,0 +1,116 @@
After setting up the Otel collector agent, follow the steps below to instrumnet your Go Application
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,21 @@
Once you are done intrumenting your Go Gin application, you can run it using the below commands
### Step 1: Run OTel Collector
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
```bash
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
```
### (Optional Step): View last 50 lines of `otelcol` logs
```bash
tail -f -n 50 otelcol-output.log
```
### (Optional Step): Stop `otelcol`
```bash
kill "$(< otel-pid)"
```
### Step 2: Set environment variables and run your Go Gin application
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=true OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 go run main.go
```

View File

@ -0,0 +1,115 @@
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,5 @@
To run your Go Gin application, use the below command :
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=false OTEL_EXPORTER_OTLP_HEADERS=signoz-access-token={{SIGNOZ_INGESTION_KEY}} OTEL_EXPORTER_OTLP_ENDPOINT=ingest.{{REGION}}.signoz.cloud:443 go run main.go
```

View File

@ -0,0 +1,93 @@
### Setup OpenTelemetry Binary as an agent
### Step 1: Download otel-collector tar.gz
```bash
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_linux_arm64.tar.gz
```
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
```bash
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_linux_arm64.tar.gz -C otelcol-contrib
```
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
```bash
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
hostmetrics:
collection_interval: 60s
scrapers:
cpu: {}
disk: {}
load: {}
filesystem: {}
memory: {}
network: {}
paging: {}
process:
mute_process_name_error: true
mute_process_exe_error: true
mute_process_io_error: true
processes: {}
prometheus:
config:
global:
scrape_interval: 60s
scrape_configs:
- job_name: otel-collector-binary
static_configs:
- targets:
# - localhost:8888
processors:
batch:
send_batch_size: 1000
timeout: 10s
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
resourcedetection:
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
timeout: 2s
system:
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
extensions:
health_check: {}
zpages: {}
exporters:
otlp:
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
tls:
insecure: false
headers:
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
logging:
verbosity: normal
service:
telemetry:
metrics:
address: 0.0.0.0:8888
extensions: [health_check, zpages]
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
metrics/internal:
receivers: [prometheus, hostmetrics]
processors: [resourcedetection, batch]
exporters: [otlp]
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
```

View File

@ -0,0 +1,116 @@
After setting up the Otel collector agent, follow the steps below to instrumnet your Go Application
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,21 @@
Once you are done intrumenting your Go Gin application, you can run it using the below commands
### Step 1: Run OTel Collector
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
```bash
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
```
### (Optional Step): View last 50 lines of `otelcol` logs
```bash
tail -f -n 50 otelcol-output.log
```
### (Optional Step): Stop `otelcol`
```bash
kill "$(< otel-pid)"
```
### Step 2: Set environment variables and run your Go Gin application
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=true OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 go run main.go
```

View File

@ -0,0 +1,115 @@
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,5 @@
To run your Go Gin application, use the below command :
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=false OTEL_EXPORTER_OTLP_HEADERS=signoz-access-token={{SIGNOZ_INGESTION_KEY}} OTEL_EXPORTER_OTLP_ENDPOINT=ingest.{{REGION}}.signoz.cloud:443 go run main.go
```

View File

@ -0,0 +1,93 @@
### Setup OpenTelemetry Binary as an agent
### Step 1: Download otel-collector tar.gz
```bash
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_darwin_amd64.tar.gz
```
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
```bash
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_darwin_amd64.tar.gz -C otelcol-contrib
```
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
```bash
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
hostmetrics:
collection_interval: 60s
scrapers:
cpu: {}
disk: {}
load: {}
filesystem: {}
memory: {}
network: {}
paging: {}
process:
mute_process_name_error: true
mute_process_exe_error: true
mute_process_io_error: true
processes: {}
prometheus:
config:
global:
scrape_interval: 60s
scrape_configs:
- job_name: otel-collector-binary
static_configs:
- targets:
# - localhost:8888
processors:
batch:
send_batch_size: 1000
timeout: 10s
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
resourcedetection:
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
timeout: 2s
system:
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
extensions:
health_check: {}
zpages: {}
exporters:
otlp:
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
tls:
insecure: false
headers:
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
logging:
verbosity: normal
service:
telemetry:
metrics:
address: 0.0.0.0:8888
extensions: [health_check, zpages]
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
metrics/internal:
receivers: [prometheus, hostmetrics]
processors: [resourcedetection, batch]
exporters: [otlp]
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
```

View File

@ -0,0 +1,116 @@
After setting up the Otel collector agent, follow the steps below to instrumnet your Go Application
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,21 @@
Once you are done intrumenting your Go Gin application, you can run it using the below commands
### Step 1: Run OTel Collector
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
```bash
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
```
### (Optional Step): View last 50 lines of `otelcol` logs
```bash
tail -f -n 50 otelcol-output.log
```
### (Optional Step): Stop `otelcol`
```bash
kill "$(< otel-pid)"
```
### Step 2: Set environment variables and run your Go Gin application
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=true OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 go run main.go
```

View File

@ -0,0 +1,115 @@
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,5 @@
To run your Go Gin application, use the below command :
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=false OTEL_EXPORTER_OTLP_HEADERS=signoz-access-token={{SIGNOZ_INGESTION_KEY}} OTEL_EXPORTER_OTLP_ENDPOINT=ingest.{{REGION}}.signoz.cloud:443 go run main.go
```

View File

@ -0,0 +1,92 @@
### Setup OpenTelemetry Binary as an agent
### Step 1: Download otel-collector tar.gz
```bash
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_darwin_arm64.tar.gz
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
```bash
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_darwin_arm64.tar.gz -C otelcol-contrib
```
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
```bash
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
hostmetrics:
collection_interval: 60s
scrapers:
cpu: {}
disk: {}
load: {}
filesystem: {}
memory: {}
network: {}
paging: {}
process:
mute_process_name_error: true
mute_process_exe_error: true
mute_process_io_error: true
processes: {}
prometheus:
config:
global:
scrape_interval: 60s
scrape_configs:
- job_name: otel-collector-binary
static_configs:
- targets:
# - localhost:8888
processors:
batch:
send_batch_size: 1000
timeout: 10s
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
resourcedetection:
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
timeout: 2s
system:
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
extensions:
health_check: {}
zpages: {}
exporters:
otlp:
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
tls:
insecure: false
headers:
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
logging:
verbosity: normal
service:
telemetry:
metrics:
address: 0.0.0.0:8888
extensions: [health_check, zpages]
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
metrics/internal:
receivers: [prometheus, hostmetrics]
processors: [resourcedetection, batch]
exporters: [otlp]
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
```

View File

@ -0,0 +1,116 @@
After setting up the Otel collector agent, follow the steps below to instrumnet your Go Application
### Step 1: Install OpenTelemetry Dependencies
Dependencies related to OpenTelemetry exporter and SDK have to be installed first
Run the below commands after navigating to the application source folder:
```bash
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
```
**Note:** Note that we are assuming you are using gin request router. If you are using other request routers, check out the [corresponding package](https://signoz.io/docs/instrumentation/golang/#request-routers).
### Step 2: Declare environment variables for configuring OpenTelemetry
Declare the following global variables in **`main.go`** which we will use to configure OpenTelemetry:
```bash
var (
serviceName = os.Getenv("SERVICE_NAME")
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
insecure = os.Getenv("INSECURE_MODE")
)
```
### Step 3: Instrument your Go application
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
```bash
import (
.....
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() func(context.Context) error {
var secureOption otlptracegrpc.Option
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
} else {
secureOption = otlptracegrpc.WithInsecure()
}
exporter, err := otlptrace.New(
context.Background(),
otlptracegrpc.NewClient(
secureOption,
otlptracegrpc.WithEndpoint(collectorURL),
),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
resources, err := resource.New(
context.Background(),
resource.WithAttributes(
attribute.String("service.name", serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
log.Fatalf("Could not set resources: %v", err)
}
otel.SetTracerProvider(
sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
),
)
return exporter.Shutdown
}
```
### Step 4: Initialise the tracer in `main.go`
Modify the main function to initialise the tracer in main.go. Initiate the tracer at the very beginning of our main function.
```bash
func main() {
cleanup := initTracer()
defer cleanup(context.Background())
......
}
```
### Step 5: Add the OpenTelemetry Gin middleware
Configure Gin to use the middleware by adding the following lines in `main.go`
```bash
import (
....
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
......
r := gin.Default()
r.Use(otelgin.Middleware(serviceName))
......
}
```

View File

@ -0,0 +1,21 @@
Once you are done intrumenting your Go Gin application, you can run it using the below commands
### Step 1: Run OTel Collector
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
```bash
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
```
### (Optional Step): View last 50 lines of `otelcol` logs
```bash
tail -f -n 50 otelcol-output.log
```
### (Optional Step): Stop `otelcol`
```bash
kill "$(< otel-pid)"
```
### Step 2: Set environment variables and run your Go Gin application
```bash
SERVICE_NAME={{MYAPP}} INSECURE_MODE=true OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 go run main.go
```

View File

@ -0,0 +1,21 @@
### Install otel-collector in your Kubernetes infra
Add the SigNoz Helm Chart repository
```bash
helm repo add signoz https://charts.signoz.io
```
If the chart is already present, update the chart to the latest using:
```bash
helm repo update
```
Install the Kubernetes Infrastructure chart provided by SigNoz
```bash
helm install my-release signoz/k8s-infra \
--set otelCollectorEndpoint=ingest.{{REGION}}.signoz.cloud:443 \
--set otelInsecure=false \
--set signozApiKey={{SIGNOZ_INGESTION_KEY}} \
--set global.clusterName=<CLUSTER_NAME>
```
- Replace `<CLUSTER_NAME>` with the name of the Kubernetes cluster or a unique identifier of the cluster.

View File

@ -0,0 +1,10 @@
After setting up the Otel collector agent, follow the steps below to instrumnet your JavaScript Application
#### Requirements
- Java 8 or higher
### Download otel java binary
```bash
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
```

View File

@ -0,0 +1,14 @@
### Open `standalone.conf` Configuration File
```bash
vim /opt/jboss-eap-7.1/bin/standalone.conf
```
### Update `JAVA_OPTS` environment variable
```bash
JAVA_OPTS="-javaagent:/<path>/opentelemetry-javaagent.jar"
```
<path> - update it to the path where you downloaded the Java JAR agent in previous step
**Note:**
- In case you're dockerising your application, make sure to dockerise it along with OpenTelemetry instrumentation done in previous step.

View File

@ -0,0 +1,8 @@
#### Requirements
- Java 8 or higher
### Download otel java binary
```bash
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
```

View File

@ -0,0 +1,23 @@
### Open `standalone.conf` Configuration File
```bash
vim /opt/jboss-eap-7.1/bin/standalone.conf
```
### Update `JAVA_OPTS` environment variable
Update `JAVA_OPTS` environment variable with configurations required to send data to SigNoz cloud in your configuration file.
```bash
JAVA_OPTS="-javaagent:/<path>/opentelemetry-javaagent.jar
-Dotel.exporter.otlp.endpoint=https://ingest.{{REGION}}.signoz.cloud:443
-Dotel.exporter.otlp.headers="signoz-access-token={{SIGNOZ_INGESTION_KEY}}"
-Dotel.resource.attributes="service.name={{MYAPP}}""
```
<path> - update it to the path where you downloaded the Java JAR agent in previous step
### (Optional step) Write the output/logs of standalone.sh script to a file nohup.out as a background thread
```bash
/opt/jboss-eap-7.1/bin/standalone.sh > /opt/jboss-eap-7.1/bin/nohup.out &
```

View File

@ -0,0 +1,93 @@
### Setup OpenTelemetry Binary as an agent
### Step 1: Download otel-collector tar.gz
```bash
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_linux_amd64.tar.gz
```
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
```bash
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_linux_amd64.tar.gz -C otelcol-contrib
```
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
```bash
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
hostmetrics:
collection_interval: 60s
scrapers:
cpu: {}
disk: {}
load: {}
filesystem: {}
memory: {}
network: {}
paging: {}
process:
mute_process_name_error: true
mute_process_exe_error: true
mute_process_io_error: true
processes: {}
prometheus:
config:
global:
scrape_interval: 60s
scrape_configs:
- job_name: otel-collector-binary
static_configs:
- targets:
# - localhost:8888
processors:
batch:
send_batch_size: 1000
timeout: 10s
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
resourcedetection:
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
timeout: 2s
system:
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
extensions:
health_check: {}
zpages: {}
exporters:
otlp:
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
tls:
insecure: false
headers:
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
logging:
verbosity: normal
service:
telemetry:
metrics:
address: 0.0.0.0:8888
extensions: [health_check, zpages]
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
metrics/internal:
receivers: [prometheus, hostmetrics]
processors: [resourcedetection, batch]
exporters: [otlp]
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
```

View File

@ -0,0 +1,10 @@
After setting up the Otel collector agent, follow the steps below to instrumnet your JavaScript Application
#### Requirements
- Java 8 or higher
### Download otel java binary
```bash
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
```

View File

@ -0,0 +1,30 @@
Once you are done intrumenting your Java application, you can run it using the below commands
### Step 1: Run OTel Collector
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
```bash
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
```
### (Optional Step): View last 50 lines of `otelcol` logs
```bash
tail -f -n 50 otelcol-output.log
```
### (Optional Step): Stop `otelcol`
```bash
kill "$(< otel-pid)"
```
### Step 2: Open `standalone.conf` Configuration File
```bash
vim /opt/jboss-eap-7.1/bin/standalone.conf
```
### Step 3: Update `JAVA_OPTS` environment variable
```bash
JAVA_OPTS="-javaagent:/<path>/opentelemetry-javaagent.jar"
```
<path> - update it to the path where you downloaded the Java JAR agent in previous step

View File

@ -0,0 +1,8 @@
#### Requirements
- Java 8 or higher
### Download otel java binary
```bash
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
```

Some files were not shown because too many files have changed in this diff Show More