Compare commits

..

5 Commits

Author SHA1 Message Date
Ishan Uniyal
e71b1d976f Revert "feat: test css commit"
This reverts commit f0a34dd460.
2026-02-27 06:29:43 +05:30
Ishan Uniyal
57c62d7230 feat: test css commit 2026-02-27 06:29:43 +05:30
Ishan Uniyal
890c516807 feat: css hover updates in log explorer 2026-02-27 06:29:43 +05:30
Srikanth Chekuri
43933f3a33 chore: move converter/formatter to pkg/units/... (#10408)
Some checks are pending
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
build-staging / staging (push) Blocked by required conditions
Release Drafter / update_release_draft (push) Waiting to run
2026-02-26 23:52:58 +05:30
Naman Verma
d2e1a24b20 chore: choose latest seen unit for metrics instead of any unit (#10431) 2026-02-26 16:48:22 +00:00
56 changed files with 323 additions and 440 deletions

View File

@@ -26,7 +26,7 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/utils/times"
"github.com/SigNoz/signoz/pkg/query-service/utils/timestamp"
"github.com/SigNoz/signoz/pkg/query-service/formatter"
"github.com/SigNoz/signoz/pkg/units"
baserules "github.com/SigNoz/signoz/pkg/query-service/rules"
@@ -335,7 +335,7 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) {
prevState := r.State()
valueFormatter := formatter.FromUnit(r.Unit())
valueFormatter := units.FormatterFromUnit(r.Unit())
var res ruletypes.Vector
var err error

View File

@@ -70,9 +70,6 @@
padding-left: 0;
}
transition: background-color 0.2s ease-in;
&:hover {
background-color: rgba(171, 189, 255, 0.04) !important;
}
}
.log-selected-fields {
@@ -183,11 +180,6 @@
.log-value {
color: var(--text-slate-400);
}
.log-line {
&:hover {
background-color: var(--text-vanilla-200) !important;
}
}
}
.dark {

View File

@@ -49,6 +49,12 @@ export const Container = styled(Card)<{
${({ $isActiveLog, $isDarkMode, $logType }): string =>
getActiveLogBackground($isActiveLog, $isDarkMode, $logType)}
}
&:hover .ant-card-body {
${({ $isDarkMode, $logType }): string =>
getActiveLogBackground(true, $isDarkMode, $logType)}
}
`;
export const LogContainer = styled.div<LogContainerProps>`

View File

@@ -8,7 +8,6 @@ import styled from 'styled-components';
import {
getActiveLogBackground,
getCustomHighlightBackground,
getDefaultLogBackground,
} from 'utils/logs';
import { RawLogContentProps } from './types';
@@ -48,7 +47,9 @@ export const RawLogViewContainer = styled(Row)<{
${({ $isReadOnly, $isActiveLog, $isDarkMode, $logType }): string =>
$isActiveLog
? getActiveLogBackground($isActiveLog, $isDarkMode, $logType)
: getDefaultLogBackground($isReadOnly, $isDarkMode)}
: !$isReadOnly
? `&:hover { ${getActiveLogBackground(true, $isDarkMode, $logType)} }`
: ''}
${({ $isHightlightedLog, $isDarkMode }): string =>
$isHightlightedLog

View File

@@ -453,9 +453,6 @@ function K8sClustersList({
const handleRowClick = (record: K8sClustersRowData): void => {
if (groupBy.length === 0) {
if (!record.clusterNameRaw) {
return;
}
setSelectedRowData(null);
setselectedClusterName(record.clusterUID);
setSearchParams({
@@ -520,13 +517,9 @@ function K8sClustersList({
showHeader={false}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.clusterNameRaw) {
setselectedClusterName(record.clusterUID);
}
setselectedClusterName(record.clusterUID);
},
className: record.clusterNameRaw
? 'expanded-clickable-row'
: 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -716,10 +709,7 @@ function K8sClustersList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.clusterNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -47,7 +47,6 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sClustersRowData {
key: string;
clusterUID: string;
clusterNameRaw: string;
clusterName: React.ReactNode;
cpu: React.ReactNode;
memory: React.ReactNode;
@@ -176,7 +175,6 @@ export const formatDataForTable = (
data.map((cluster, index) => ({
key: index.toString(),
clusterUID: cluster.meta.k8s_cluster_name,
clusterNameRaw: cluster.meta.k8s_cluster_name || '',
clusterName: (
<Tooltip title={cluster.meta.k8s_cluster_name}>
{cluster.meta.k8s_cluster_name}

View File

@@ -459,9 +459,6 @@ function K8sDaemonSetsList({
const handleRowClick = (record: K8sDaemonSetsRowData): void => {
if (groupBy.length === 0) {
if (!record.daemonsetNameRaw) {
return;
}
setSelectedRowData(null);
setSelectedDaemonSetUID(record.daemonsetUID);
setSearchParams({
@@ -526,13 +523,9 @@ function K8sDaemonSetsList({
showHeader={false}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.daemonsetNameRaw) {
setSelectedDaemonSetUID(record.daemonsetUID);
}
setSelectedDaemonSetUID(record.daemonsetUID);
},
className: record.daemonsetNameRaw
? 'expanded-clickable-row'
: 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -724,10 +717,7 @@ function K8sDaemonSetsList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.daemonsetNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -82,7 +82,6 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sDaemonSetsRowData {
key: string;
daemonsetUID: string;
daemonsetNameRaw: string;
daemonsetName: React.ReactNode;
cpu_request: React.ReactNode;
cpu_limit: React.ReactNode;
@@ -277,7 +276,6 @@ export const formatDataForTable = (
data.map((daemonSet, index) => ({
key: index.toString(),
daemonsetUID: daemonSet.daemonSetName,
daemonsetNameRaw: daemonSet.meta.k8s_daemonset_name || '',
daemonsetName: (
<Tooltip title={daemonSet.meta.k8s_daemonset_name}>
{daemonSet.meta.k8s_daemonset_name || ''}

View File

@@ -465,9 +465,6 @@ function K8sDeploymentsList({
const handleRowClick = (record: K8sDeploymentsRowData): void => {
if (groupBy.length === 0) {
if (!record.deploymentNameRaw) {
return;
}
setSelectedRowData(null);
setselectedDeploymentUID(record.deploymentUID);
setSearchParams({
@@ -532,13 +529,9 @@ function K8sDeploymentsList({
showHeader={false}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.deploymentNameRaw) {
setselectedDeploymentUID(record.deploymentUID);
}
setselectedDeploymentUID(record.deploymentUID);
},
className: record.deploymentNameRaw
? 'expanded-clickable-row'
: 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -731,10 +724,7 @@ function K8sDeploymentsList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.deploymentNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -81,7 +81,6 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sDeploymentsRowData {
key: string;
deploymentUID: string;
deploymentNameRaw: string;
deploymentName: React.ReactNode;
available_pods: React.ReactNode;
desired_pods: React.ReactNode;
@@ -268,7 +267,6 @@ export const formatDataForTable = (
data.map((deployment, index) => ({
key: index.toString(),
deploymentUID: deployment.meta.k8s_deployment_name,
deploymentNameRaw: deployment.meta.k8s_deployment_name || '',
deploymentName: (
<Tooltip title={deployment.meta.k8s_deployment_name}>
{deployment.meta.k8s_deployment_name}

View File

@@ -337,11 +337,6 @@
cursor: pointer;
}
.disabled-row {
cursor: default;
opacity: 0.6;
}
.k8s-list-table {
.ant-table {
.ant-table-thead > tr > th {

View File

@@ -430,9 +430,6 @@ function K8sJobsList({
const handleRowClick = (record: K8sJobsRowData): void => {
if (groupBy.length === 0) {
if (!record.jobNameRaw) {
return;
}
setSelectedRowData(null);
setselectedJobUID(record.jobUID);
setSearchParams({
@@ -497,11 +494,9 @@ function K8sJobsList({
showHeader={false}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.jobNameRaw) {
setselectedJobUID(record.jobUID);
}
setselectedJobUID(record.jobUID);
},
className: record.jobNameRaw ? 'expanded-clickable-row' : 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -691,10 +686,7 @@ function K8sJobsList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.jobNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -94,7 +94,6 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sJobsRowData {
key: string;
jobUID: string;
jobNameRaw: string;
jobName: React.ReactNode;
namespaceName: React.ReactNode;
successful_pods: React.ReactNode;
@@ -304,7 +303,6 @@ export const formatDataForTable = (
data.map((job, index) => ({
key: index.toString(),
jobUID: job.jobName,
jobNameRaw: job.meta.k8s_job_name || '',
jobName: (
<Tooltip title={job.meta.k8s_job_name}>
{job.meta.k8s_job_name || ''}

View File

@@ -461,9 +461,6 @@ function K8sNamespacesList({
const handleRowClick = (record: K8sNamespacesRowData): void => {
if (groupBy.length === 0) {
if (!record.namespaceNameRaw) {
return;
}
setSelectedRowData(null);
setselectedNamespaceUID(record.namespaceUID);
setSearchParams({
@@ -528,13 +525,9 @@ function K8sNamespacesList({
showHeader={false}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.namespaceNameRaw) {
setselectedNamespaceUID(record.namespaceUID);
}
setselectedNamespaceUID(record.namespaceUID);
},
className: record.namespaceNameRaw
? 'expanded-clickable-row'
: 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -725,10 +718,7 @@ function K8sNamespacesList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.namespaceNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -41,7 +41,6 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sNamespacesRowData {
key: string;
namespaceUID: string;
namespaceNameRaw: string;
namespaceName: string;
clusterName: string;
cpu: React.ReactNode;
@@ -162,7 +161,6 @@ export const formatDataForTable = (
data.map((namespace, index) => ({
key: index.toString(),
namespaceUID: namespace.namespaceName,
namespaceNameRaw: namespace.namespaceName || '',
namespaceName: namespace.namespaceName,
clusterName: namespace.meta.k8s_cluster_name,
cpu: (

View File

@@ -440,9 +440,6 @@ function K8sNodesList({
const handleRowClick = (record: K8sNodesRowData): void => {
if (groupBy.length === 0) {
if (!record.nodeNameRaw) {
return;
}
setSelectedRowData(null);
setSelectedNodeUID(record.nodeUID);
setSearchParams({
@@ -508,13 +505,9 @@ function K8sNodesList({
showHeader={false}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.nodeNameRaw) {
setSelectedNodeUID(record.nodeUID);
}
setSelectedNodeUID(record.nodeUID);
},
className: record.nodeNameRaw
? 'expanded-clickable-row'
: 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -704,10 +697,7 @@ function K8sNodesList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.nodeNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -53,7 +53,6 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sNodesRowData {
key: string;
nodeUID: string;
nodeNameRaw: string;
nodeName: React.ReactNode;
clusterName: string;
cpu: React.ReactNode;
@@ -194,7 +193,6 @@ export const formatDataForTable = (
data.map((node, index) => ({
key: `${node.nodeUID}-${index}`,
nodeUID: node.nodeUID || '',
nodeNameRaw: node.meta.k8s_node_name || '',
nodeName: (
<Tooltip title={node.meta.k8s_node_name}>
{node.meta.k8s_node_name || ''}

View File

@@ -497,9 +497,6 @@ function K8sPodsList({
const handleRowClick = (record: K8sPodsRowData): void => {
if (groupBy.length === 0) {
if (!record.podNameRaw) {
return;
}
setSelectedPodUID(record.podUID);
setSearchParams({
...Object.fromEntries(searchParams.entries()),
@@ -620,11 +617,9 @@ function K8sPodsList({
}}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.podNameRaw) {
setSelectedPodUID(record.podUID);
}
setSelectedPodUID(record.podUID);
},
className: record.podNameRaw ? 'expanded-clickable-row' : 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -759,10 +754,7 @@ function K8sPodsList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.podNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -462,9 +462,6 @@ function K8sStatefulSetsList({
const handleRowClick = (record: K8sStatefulSetsRowData): void => {
if (groupBy.length === 0) {
if (!record.statefulsetNameRaw) {
return;
}
setSelectedRowData(null);
setselectedStatefulSetUID(record.statefulsetUID);
setSearchParams({
@@ -529,13 +526,9 @@ function K8sStatefulSetsList({
showHeader={false}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.statefulsetNameRaw) {
setselectedStatefulSetUID(record.statefulsetUID);
}
setselectedStatefulSetUID(record.statefulsetUID);
},
className: record.statefulsetNameRaw
? 'expanded-clickable-row'
: 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -727,10 +720,7 @@ function K8sStatefulSetsList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.statefulsetNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -82,7 +82,6 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sStatefulSetsRowData {
key: string;
statefulsetUID: string;
statefulsetNameRaw: string;
statefulsetName: React.ReactNode;
cpu_request: React.ReactNode;
cpu_limit: React.ReactNode;
@@ -277,7 +276,6 @@ export const formatDataForTable = (
data.map((statefulSet, index) => ({
key: index.toString(),
statefulsetUID: statefulSet.statefulSetName,
statefulsetNameRaw: statefulSet.meta.k8s_statefulset_name || '',
statefulsetName: (
<Tooltip title={statefulSet.meta.k8s_statefulset_name}>
{statefulSet.meta.k8s_statefulset_name || ''}

View File

@@ -392,9 +392,6 @@ function K8sVolumesList({
const handleRowClick = (record: K8sVolumesRowData): void => {
if (groupBy.length === 0) {
if (!record.volumeNameRaw) {
return;
}
setSelectedRowData(null);
setselectedVolumeUID(record.volumeUID);
setSearchParams({
@@ -459,13 +456,9 @@ function K8sVolumesList({
showHeader={false}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (record.volumeNameRaw) {
setselectedVolumeUID(record.volumeUID);
}
setselectedVolumeUID(record.volumeUID);
},
className: record.volumeNameRaw
? 'expanded-clickable-row'
: 'disabled-row',
className: 'expanded-clickable-row',
})}
/>
@@ -650,10 +643,7 @@ function K8sVolumesList({
onChange={handleTableChange}
onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => handleRowClick(record),
className:
groupBy.length > 0 || record.volumeNameRaw
? 'clickable-row'
: 'disabled-row',
className: 'clickable-row',
})}
expandable={{
expandedRowRender: isGroupedByAttribute ? expandedRowRender : undefined,

View File

@@ -47,7 +47,6 @@ export const defaultAddedColumns: IEntityColumn[] = [
export interface K8sVolumesRowData {
key: string;
volumeUID: string;
volumeNameRaw: string;
pvcName: React.ReactNode;
namespaceName: React.ReactNode;
capacity: React.ReactNode;
@@ -187,7 +186,6 @@ export const formatDataForTable = (
data.map((volume, index) => ({
key: index.toString(),
volumeUID: volume.persistentVolumeClaimName,
volumeNameRaw: volume.persistentVolumeClaimName || '',
pvcName: (
<Tooltip title={volume.persistentVolumeClaimName}>
{volume.persistentVolumeClaimName || ''}

View File

@@ -107,7 +107,6 @@ export const defaultAvailableColumns = [
export interface K8sPodsRowData {
key: string;
podName: React.ReactNode;
podNameRaw: string;
podUID: string;
cpu_request: React.ReactNode;
cpu_limit: React.ReactNode;
@@ -351,7 +350,6 @@ export const formatDataForTable = (
{pod.meta.k8s_pod_name || ''}
</Tooltip>
),
podNameRaw: pod.meta.k8s_pod_name || '',
podUID: pod.podUID || '',
cpu_request: (
<ValidateColumnValueWrapper

View File

@@ -70,12 +70,8 @@ export const TableRowStyled = styled.tr<{
&:hover {
${TableCellStyled} {
${({ $isActiveLog, $isDarkMode }): string =>
$isActiveLog
? getActiveLogBackground()
: `background-color: ${
!$isDarkMode ? 'var(--bg-vanilla-200)' : 'rgba(171, 189, 255, 0.04)'
}`}
${({ $isDarkMode, $logType }): string =>
getActiveLogBackground(true, $isDarkMode, $logType)}
}
.log-line-action-buttons {
display: flex;

View File

@@ -513,7 +513,7 @@ func (m *module) fetchTimeseriesMetadata(ctx context.Context, orgID valuer.UUID,
"metric_name",
"anyLast(description) AS description",
"anyLast(type) AS metric_type",
"anyLast(unit) AS metric_unit",
"argMax(unit, unix_milli) AS metric_unit",
"anyLast(temporality) AS temporality",
"anyLast(is_monotonic) AS is_monotonic",
)

View File

@@ -5441,7 +5441,7 @@ func (r *ClickHouseReader) ListSummaryMetrics(ctx context.Context, orgID valuer.
t.metric_name AS metric_name,
ANY_VALUE(t.description) AS description,
ANY_VALUE(t.type) AS metric_type,
ANY_VALUE(t.unit) AS metric_unit,
argMax(t.unit, unix_milli) AS metric_unit,
uniq(t.fingerprint) AS timeseries,
uniq(metric_name) OVER() AS total
FROM %s.%s AS t

View File

@@ -1,86 +0,0 @@
package formatter
import (
"fmt"
"github.com/SigNoz/signoz/pkg/query-service/converter"
"github.com/dustin/go-humanize"
)
type dataFormatter struct {
}
func NewDataFormatter() Formatter {
return &dataFormatter{}
}
func (*dataFormatter) Name() string {
return "data"
}
func (f *dataFormatter) Format(value float64, unit string) string {
switch unit {
case "bytes", "By":
return humanize.IBytes(uint64(value))
case "decbytes":
return humanize.Bytes(uint64(value))
case "bits", "bit":
// humanize.IBytes/Bytes doesn't support bits
// and returns 0 B for values less than a byte
if value < 8 {
return fmt.Sprintf("%v b", value)
}
return humanize.IBytes(uint64(value / 8))
case "decbits":
if value < 8 {
return fmt.Sprintf("%v b", value)
}
return humanize.Bytes(uint64(value / 8))
case "kbytes", "KiBy":
return humanize.IBytes(uint64(value * converter.Kibibit))
case "Kibit":
return humanize.IBytes(uint64(value * converter.Kibibit / 8))
case "decKbytes", "deckbytes", "kBy":
return humanize.Bytes(uint64(value * converter.Kilobit))
case "kbit":
return humanize.Bytes(uint64(value * converter.Kilobit / 8))
case "mbytes", "MiBy":
return humanize.IBytes(uint64(value * converter.Mebibit))
case "Mibit":
return humanize.IBytes(uint64(value * converter.Mebibit / 8))
case "decMbytes", "decmbytes", "MBy":
return humanize.Bytes(uint64(value * converter.Megabit))
case "Mbit":
return humanize.Bytes(uint64(value * converter.Megabit / 8))
case "gbytes", "GiBy":
return humanize.IBytes(uint64(value * converter.Gibibit))
case "Gibit":
return humanize.IBytes(uint64(value * converter.Gibibit / 8))
case "decGbytes", "decgbytes", "GBy":
return humanize.Bytes(uint64(value * converter.Gigabit))
case "Gbit":
return humanize.Bytes(uint64(value * converter.Gigabit / 8))
case "tbytes", "TiBy":
return humanize.IBytes(uint64(value * converter.Tebibit))
case "Tibit":
return humanize.IBytes(uint64(value * converter.Tebibit / 8))
case "decTbytes", "dectbytes", "TBy":
return humanize.Bytes(uint64(value * converter.Terabit))
case "Tbit":
return humanize.Bytes(uint64(value * converter.Terabit / 8))
case "pbytes", "PiBy":
return humanize.IBytes(uint64(value * converter.Pebibit))
case "Pbit":
return humanize.Bytes(uint64(value * converter.Petabit / 8))
case "decPbytes", "decpbytes", "PBy":
return humanize.Bytes(uint64(value * converter.Petabit))
case "EiBy":
return humanize.IBytes(uint64(value * converter.Exbibit))
case "Ebit":
return humanize.Bytes(uint64(value * converter.Exabit / 8))
case "EBy":
return humanize.Bytes(uint64(value * converter.Exabit))
}
// When unit is not matched, return the value as it is.
return fmt.Sprintf("%v", value)
}

View File

@@ -1,91 +0,0 @@
package formatter
import (
"fmt"
"github.com/SigNoz/signoz/pkg/query-service/converter"
"github.com/dustin/go-humanize"
)
type dataRateFormatter struct {
}
func NewDataRateFormatter() Formatter {
return &dataRateFormatter{}
}
func (*dataRateFormatter) Name() string {
return "data_rate"
}
func (f *dataRateFormatter) Format(value float64, unit string) string {
switch unit {
case "binBps":
return humanize.IBytes(uint64(value)) + "/s"
case "Bps", "By/s":
return humanize.Bytes(uint64(value)) + "/s"
case "binbps":
// humanize.IBytes/Bytes doesn't support bits
// and returns 0 B for values less than a byte
if value < 8 {
return fmt.Sprintf("%v b/s", value)
}
return humanize.IBytes(uint64(value/8)) + "/s"
case "bps", "bit/s":
if value < 8 {
return fmt.Sprintf("%v b/s", value)
}
return humanize.Bytes(uint64(value/8)) + "/s"
case "KiBs", "KiBy/s":
return humanize.IBytes(uint64(value*converter.KibibitPerSecond)) + "/s"
case "Kibits", "Kibit/s":
return humanize.IBytes(uint64(value*converter.KibibitPerSecond/8)) + "/s"
case "KBs", "kBy/s":
return humanize.IBytes(uint64(value*converter.KilobitPerSecond)) + "/s"
case "Kbits", "kbit/s":
return humanize.Bytes(uint64(value*converter.KilobitPerSecond/8)) + "/s"
case "MiBs", "MiBy/s":
return humanize.IBytes(uint64(value*converter.MebibitPerSecond)) + "/s"
case "Mibits", "Mibit/s":
return humanize.IBytes(uint64(value*converter.MebibitPerSecond/8)) + "/s"
case "MBs", "MBy/s":
return humanize.IBytes(uint64(value*converter.MegabitPerSecond)) + "/s"
case "Mbits", "Mbit/s":
return humanize.Bytes(uint64(value*converter.MegabitPerSecond/8)) + "/s"
case "GiBs", "GiBy/s":
return humanize.IBytes(uint64(value*converter.GibibitPerSecond)) + "/s"
case "Gibits", "Gibit/s":
return humanize.IBytes(uint64(value*converter.GibibitPerSecond/8)) + "/s"
case "GBs", "GBy/s":
return humanize.IBytes(uint64(value*converter.GigabitPerSecond)) + "/s"
case "Gbits", "Gbit/s":
return humanize.Bytes(uint64(value*converter.GigabitPerSecond/8)) + "/s"
case "TiBs", "TiBy/s":
return humanize.IBytes(uint64(value*converter.TebibitPerSecond)) + "/s"
case "Tibits", "Tibit/s":
return humanize.IBytes(uint64(value*converter.TebibitPerSecond/8)) + "/s"
case "TBs", "TBy/s":
return humanize.IBytes(uint64(value*converter.TerabitPerSecond)) + "/s"
case "Tbits", "Tbit/s":
return humanize.Bytes(uint64(value*converter.TerabitPerSecond/8)) + "/s"
case "PiBs", "PiBy/s":
return humanize.IBytes(uint64(value*converter.PebibitPerSecond)) + "/s"
case "Pibits", "Pibit/s":
return humanize.IBytes(uint64(value*converter.PebibitPerSecond/8)) + "/s"
case "PBs", "PBy/s":
return humanize.IBytes(uint64(value*converter.PetabitPerSecond)) + "/s"
case "Pbits", "Pbit/s":
return humanize.Bytes(uint64(value*converter.PetabitPerSecond/8)) + "/s"
// Exa units
case "EBy/s":
return humanize.Bytes(uint64(value*converter.ExabitPerSecond)) + "/s"
case "Ebit/s":
return humanize.Bytes(uint64(value*converter.ExabitPerSecond/8)) + "/s"
case "EiBy/s":
return humanize.IBytes(uint64(value*converter.ExbibitPerSecond)) + "/s"
case "Eibit/s":
return humanize.IBytes(uint64(value*converter.ExbibitPerSecond/8)) + "/s"
}
// When unit is not matched, return the value as it is.
return fmt.Sprintf("%v", value)
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/query-service/formatter"
"github.com/SigNoz/signoz/pkg/units"
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
@@ -185,7 +185,7 @@ func (r *PromRule) buildAndRunQuery(ctx context.Context, ts time.Time) (ruletype
func (r *PromRule) Eval(ctx context.Context, ts time.Time) (int, error) {
prevState := r.State()
valueFormatter := formatter.FromUnit(r.Unit())
valueFormatter := units.FormatterFromUnit(r.Unit())
// prepare query, run query get data and filter the data based on the threshold
results, err := r.buildAndRunQuery(ctx, ts)

View File

@@ -33,7 +33,7 @@ import (
logsv3 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v3"
tracesV4 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v4"
"github.com/SigNoz/signoz/pkg/query-service/formatter"
"github.com/SigNoz/signoz/pkg/units"
querierV5 "github.com/SigNoz/signoz/pkg/querier"
@@ -571,7 +571,7 @@ func (r *ThresholdRule) buildAndRunQueryV5(ctx context.Context, orgID valuer.UUI
func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time) (int, error) {
prevState := r.State()
valueFormatter := formatter.FromUnit(r.Unit())
valueFormatter := units.FormatterFromUnit(r.Unit())
var res ruletypes.Vector
var err error

View File

@@ -7,7 +7,7 @@ import (
"strings"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/query-service/converter"
"github.com/SigNoz/signoz/pkg/units"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/utils/labels"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -201,12 +201,12 @@ func sortThresholds(thresholds []BasicRuleThreshold) {
// convertToRuleUnit converts the given value from the target unit to the rule unit
func (b BasicRuleThreshold) convertToRuleUnit(val float64, ruleUnit string) float64 {
unitConverter := converter.FromUnit(converter.Unit(b.TargetUnit))
unitConverter := units.ConverterFromUnit(units.Unit(b.TargetUnit))
// convert the target value to the y-axis unit
value := unitConverter.Convert(converter.Value{
value := unitConverter.Convert(units.Value{
F: val,
U: converter.Unit(b.TargetUnit),
}, converter.Unit(ruleUnit))
U: units.Unit(b.TargetUnit),
}, units.Unit(ruleUnit))
return value.F
}

View File

@@ -1,4 +1,4 @@
package converter
package units
// Unit represents a unit of measurement
type Unit string
@@ -40,8 +40,8 @@ var (
NoneConverter = &noneConverter{}
)
// FromUnit returns a converter for the given unit
func FromUnit(u Unit) Converter {
// ConverterFromUnit returns a converter for the given unit
func ConverterFromUnit(u Unit) Converter {
switch u {
case "ns", "us", "µs", "ms", "s", "m", "h", "d", "min", "w", "wk":
return DurationConverter

View File

@@ -1,4 +1,4 @@
package converter
package units
// boolConverter is a Converter implementation for bool
type boolConverter struct{}

View File

@@ -1,4 +1,4 @@
package converter
package units
const (
// base 10 (SI prefixes)

View File

@@ -1,4 +1,4 @@
package converter
package units
const (
// base 10 (SI prefixes)

View File

@@ -1,4 +1,4 @@
package converter
package units
import (
"testing"

View File

@@ -1,4 +1,4 @@
package converter
package units
import (
"testing"

View File

@@ -1,4 +1,4 @@
package converter
package units
// percentConverter is a converter for percent unit
type percentConverter struct{}

View File

@@ -1,4 +1,4 @@
package converter
package units
import (
"testing"

View File

@@ -1,4 +1,4 @@
package converter
package units
// throughputConverter is an implementation of Converter that converts throughput
type throughputConverter struct {

View File

@@ -1,4 +1,4 @@
package converter
package units
type Duration float64

View File

@@ -1,4 +1,4 @@
package converter
package units
import (
"testing"

View File

@@ -1,4 +1,4 @@
package formatter
package units
type Formatter interface {
Format(value float64, unit string) string
@@ -16,7 +16,7 @@ var (
ThroughputFormatter = NewThroughputFormatter()
)
func FromUnit(u string) Formatter {
func FormatterFromUnit(u string) Formatter {
switch u {
case "ns", "us", "µs", "ms", "s", "m", "h", "d", "min", "w", "wk":
return DurationFormatter

View File

@@ -1,4 +1,4 @@
package formatter
package units
import "fmt"

View File

@@ -0,0 +1,85 @@
package units
import (
"fmt"
"github.com/dustin/go-humanize"
)
type dataFormatter struct {
}
func NewDataFormatter() Formatter {
return &dataFormatter{}
}
func (*dataFormatter) Name() string {
return "data"
}
func (f *dataFormatter) Format(value float64, unit string) string {
switch unit {
case "bytes", "By":
return humanize.IBytes(uint64(value))
case "decbytes":
return humanize.Bytes(uint64(value))
case "bits", "bit":
// humanize.IBytes/Bytes doesn't support bits
// and returns 0 B for values less than a byte
if value < 8 {
return fmt.Sprintf("%v b", value)
}
return humanize.IBytes(uint64(value / 8))
case "decbits":
if value < 8 {
return fmt.Sprintf("%v b", value)
}
return humanize.Bytes(uint64(value / 8))
case "kbytes", "KiBy":
return humanize.IBytes(uint64(value * Kibibit))
case "Kibit":
return humanize.IBytes(uint64(value * Kibibit / 8))
case "decKbytes", "deckbytes", "kBy":
return humanize.Bytes(uint64(value * Kilobit))
case "kbit":
return humanize.Bytes(uint64(value * Kilobit / 8))
case "mbytes", "MiBy":
return humanize.IBytes(uint64(value * Mebibit))
case "Mibit":
return humanize.IBytes(uint64(value * Mebibit / 8))
case "decMbytes", "decmbytes", "MBy":
return humanize.Bytes(uint64(value * Megabit))
case "Mbit":
return humanize.Bytes(uint64(value * Megabit / 8))
case "gbytes", "GiBy":
return humanize.IBytes(uint64(value * Gibibit))
case "Gibit":
return humanize.IBytes(uint64(value * Gibibit / 8))
case "decGbytes", "decgbytes", "GBy":
return humanize.Bytes(uint64(value * Gigabit))
case "Gbit":
return humanize.Bytes(uint64(value * Gigabit / 8))
case "tbytes", "TiBy":
return humanize.IBytes(uint64(value * Tebibit))
case "Tibit":
return humanize.IBytes(uint64(value * Tebibit / 8))
case "decTbytes", "dectbytes", "TBy":
return humanize.Bytes(uint64(value * Terabit))
case "Tbit":
return humanize.Bytes(uint64(value * Terabit / 8))
case "pbytes", "PiBy":
return humanize.IBytes(uint64(value * Pebibit))
case "Pbit":
return humanize.Bytes(uint64(value * Petabit / 8))
case "decPbytes", "decpbytes", "PBy":
return humanize.Bytes(uint64(value * Petabit))
case "EiBy":
return humanize.IBytes(uint64(value * Exbibit))
case "Ebit":
return humanize.Bytes(uint64(value * Exabit / 8))
case "EBy":
return humanize.Bytes(uint64(value * Exabit))
}
// When unit is not matched, return the value as it is.
return fmt.Sprintf("%v", value)
}

View File

@@ -0,0 +1,90 @@
package units
import (
"fmt"
"github.com/dustin/go-humanize"
)
type dataRateFormatter struct {
}
func NewDataRateFormatter() Formatter {
return &dataRateFormatter{}
}
func (*dataRateFormatter) Name() string {
return "data_rate"
}
func (f *dataRateFormatter) Format(value float64, unit string) string {
switch unit {
case "binBps":
return humanize.IBytes(uint64(value)) + "/s"
case "Bps", "By/s":
return humanize.Bytes(uint64(value)) + "/s"
case "binbps":
// humanize.IBytes/Bytes doesn't support bits
// and returns 0 B for values less than a byte
if value < 8 {
return fmt.Sprintf("%v b/s", value)
}
return humanize.IBytes(uint64(value/8)) + "/s"
case "bps", "bit/s":
if value < 8 {
return fmt.Sprintf("%v b/s", value)
}
return humanize.Bytes(uint64(value/8)) + "/s"
case "KiBs", "KiBy/s":
return humanize.IBytes(uint64(value*KibibitPerSecond)) + "/s"
case "Kibits", "Kibit/s":
return humanize.IBytes(uint64(value*KibibitPerSecond/8)) + "/s"
case "KBs", "kBy/s":
return humanize.IBytes(uint64(value*KilobitPerSecond)) + "/s"
case "Kbits", "kbit/s":
return humanize.Bytes(uint64(value*KilobitPerSecond/8)) + "/s"
case "MiBs", "MiBy/s":
return humanize.IBytes(uint64(value*MebibitPerSecond)) + "/s"
case "Mibits", "Mibit/s":
return humanize.IBytes(uint64(value*MebibitPerSecond/8)) + "/s"
case "MBs", "MBy/s":
return humanize.IBytes(uint64(value*MegabitPerSecond)) + "/s"
case "Mbits", "Mbit/s":
return humanize.Bytes(uint64(value*MegabitPerSecond/8)) + "/s"
case "GiBs", "GiBy/s":
return humanize.IBytes(uint64(value*GibibitPerSecond)) + "/s"
case "Gibits", "Gibit/s":
return humanize.IBytes(uint64(value*GibibitPerSecond/8)) + "/s"
case "GBs", "GBy/s":
return humanize.IBytes(uint64(value*GigabitPerSecond)) + "/s"
case "Gbits", "Gbit/s":
return humanize.Bytes(uint64(value*GigabitPerSecond/8)) + "/s"
case "TiBs", "TiBy/s":
return humanize.IBytes(uint64(value*TebibitPerSecond)) + "/s"
case "Tibits", "Tibit/s":
return humanize.IBytes(uint64(value*TebibitPerSecond/8)) + "/s"
case "TBs", "TBy/s":
return humanize.IBytes(uint64(value*TerabitPerSecond)) + "/s"
case "Tbits", "Tbit/s":
return humanize.Bytes(uint64(value*TerabitPerSecond/8)) + "/s"
case "PiBs", "PiBy/s":
return humanize.IBytes(uint64(value*PebibitPerSecond)) + "/s"
case "Pibits", "Pibit/s":
return humanize.IBytes(uint64(value*PebibitPerSecond/8)) + "/s"
case "PBs", "PBy/s":
return humanize.IBytes(uint64(value*PetabitPerSecond)) + "/s"
case "Pbits", "Pbit/s":
return humanize.Bytes(uint64(value*PetabitPerSecond/8)) + "/s"
// Exa units
case "EBy/s":
return humanize.Bytes(uint64(value*ExabitPerSecond)) + "/s"
case "Ebit/s":
return humanize.Bytes(uint64(value*ExabitPerSecond/8)) + "/s"
case "EiBy/s":
return humanize.IBytes(uint64(value*ExbibitPerSecond)) + "/s"
case "Eibit/s":
return humanize.IBytes(uint64(value*ExbibitPerSecond/8)) + "/s"
}
// When unit is not matched, return the value as it is.
return fmt.Sprintf("%v", value)
}

View File

@@ -1,4 +1,4 @@
package formatter
package units
import (
"testing"

View File

@@ -1,4 +1,4 @@
package formatter
package units
import (
"testing"
@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestData(t *testing.T) {
func TestFormatterData(t *testing.T) {
dataFormatter := NewDataFormatter()
assert.Equal(t, "1 B", dataFormatter.Format(1, "bytes"))

View File

@@ -1,4 +1,4 @@
package formatter
package units
import "fmt"

View File

@@ -1,4 +1,4 @@
package formatter
package units
import "fmt"

View File

@@ -1,4 +1,4 @@
package formatter
package units
import (
"fmt"
@@ -14,36 +14,36 @@ type IntervalsInSecondsType map[Interval]int
type Interval string
const (
Year Interval = "year"
Month Interval = "month"
Week Interval = "week"
Day Interval = "day"
Hour Interval = "hour"
Minute Interval = "minute"
Second Interval = "second"
Millisecond Interval = "millisecond"
IntervalYear Interval = "year"
IntervalMonth Interval = "month"
IntervalWeek Interval = "week"
IntervalDay Interval = "day"
IntervalHour Interval = "hour"
IntervalMinute Interval = "minute"
IntervalSecond Interval = "second"
IntervalMillisecond Interval = "millisecond"
)
var Units = []Interval{
Year,
Month,
Week,
Day,
Hour,
Minute,
Second,
Millisecond,
IntervalYear,
IntervalMonth,
IntervalWeek,
IntervalDay,
IntervalHour,
IntervalMinute,
IntervalSecond,
IntervalMillisecond,
}
var IntervalsInSeconds = IntervalsInSecondsType{
Year: 31536000,
Month: 2592000,
Week: 604800,
Day: 86400,
Hour: 3600,
Minute: 60,
Second: 1,
Millisecond: 1,
IntervalYear: 31536000,
IntervalMonth: 2592000,
IntervalWeek: 604800,
IntervalDay: 86400,
IntervalHour: 3600,
IntervalMinute: 60,
IntervalSecond: 1,
IntervalMillisecond: 1,
}
type DecimalCount *int
@@ -78,9 +78,8 @@ func toFixed(value float64, decimals DecimalCount) string {
}
decimalPos := strings.Index(formatted, ".")
precision := 0
if decimalPos != -1 {
precision = len(formatted) - decimalPos - 1
precision := len(formatted) - decimalPos - 1
if precision < *decimals {
return formatted + strings.Repeat("0", *decimals-precision)
}
@@ -89,8 +88,8 @@ func toFixed(value float64, decimals DecimalCount) string {
return formatted
}
func toFixedScaled(value float64, decimals DecimalCount, scaleFormat string) string {
return toFixed(value, decimals) + scaleFormat
func toFixedScaled(value float64, scaleFormat string) string {
return toFixed(value, nil) + scaleFormat
}
func getDecimalsForValue(value float64) int {

View File

@@ -1,4 +1,4 @@
package formatter
package units
import (
"testing"

View File

@@ -1,4 +1,4 @@
package formatter
package units
import "fmt"
@@ -13,35 +13,35 @@ func (*throughputFormatter) Name() string {
return "throughput"
}
func simpleCountUnit(value float64, decimals *int, symbol string) string {
func simpleCountUnit(value float64, symbol string) string {
units := []string{"", "K", "M", "B", "T"}
scaler := scaledUnits(1000, units, 0)
return scaler(value, decimals) + " " + symbol
return scaler(value, nil) + " " + symbol
}
func (f *throughputFormatter) Format(value float64, unit string) string {
switch unit {
case "cps", "{count}/s":
return simpleCountUnit(value, nil, "c/s")
return simpleCountUnit(value, "c/s")
case "ops", "{ops}/s":
return simpleCountUnit(value, nil, "op/s")
return simpleCountUnit(value, "op/s")
case "reqps", "{req}/s":
return simpleCountUnit(value, nil, "req/s")
return simpleCountUnit(value, "req/s")
case "rps", "{read}/s":
return simpleCountUnit(value, nil, "r/s")
return simpleCountUnit(value, "r/s")
case "wps", "{write}/s":
return simpleCountUnit(value, nil, "w/s")
return simpleCountUnit(value, "w/s")
case "iops", "{iops}/s":
return simpleCountUnit(value, nil, "iops")
return simpleCountUnit(value, "iops")
case "cpm", "{count}/min":
return simpleCountUnit(value, nil, "c/m")
return simpleCountUnit(value, "c/m")
case "opm", "{ops}/min":
return simpleCountUnit(value, nil, "op/m")
return simpleCountUnit(value, "op/m")
case "rpm", "{read}/min":
return simpleCountUnit(value, nil, "r/m")
return simpleCountUnit(value, "r/m")
case "wpm", "{write}/min":
return simpleCountUnit(value, nil, "w/m")
return simpleCountUnit(value, "w/m")
}
// When unit is not matched, return the value as it is.
return fmt.Sprintf("%v", value)

View File

@@ -1,4 +1,4 @@
package formatter
package units
import (
"testing"

View File

@@ -1,4 +1,4 @@
package formatter
package units
import (
"fmt"
@@ -46,17 +46,17 @@ func toNanoSeconds(value float64) string {
if absValue < 1000 {
return toFixed(value, nil) + " ns"
} else if absValue < 1000000 { // 2000 ns is better represented as 2 µs
return toFixedScaled(value/1000, nil, " µs")
return toFixedScaled(value/1000, " µs")
} else if absValue < 1000000000 { // 2000000 ns is better represented as 2 ms
return toFixedScaled(value/1000000, nil, " ms")
return toFixedScaled(value/1000000, " ms")
} else if absValue < 60000000000 {
return toFixedScaled(value/1000000000, nil, " s")
return toFixedScaled(value/1000000000, " s")
} else if absValue < 3600000000000 {
return toFixedScaled(value/60000000000, nil, " min")
return toFixedScaled(value/60000000000, " min")
} else if absValue < 86400000000000 {
return toFixedScaled(value/3600000000000, nil, " hour")
return toFixedScaled(value/3600000000000, " hour")
} else {
return toFixedScaled(value/86400000000000, nil, " day")
return toFixedScaled(value/86400000000000, " day")
}
}
@@ -66,9 +66,9 @@ func toMicroSeconds(value float64) string {
if absValue < 1000 {
return toFixed(value, nil) + " µs"
} else if absValue < 1000000 { // 2000 µs is better represented as 2 ms
return toFixedScaled(value/1000, nil, " ms")
return toFixedScaled(value/1000, " ms")
} else {
return toFixedScaled(value/1000000, nil, " s")
return toFixedScaled(value/1000000, " s")
}
}
@@ -80,16 +80,16 @@ func toMilliSeconds(value float64) string {
if absValue < 1000 {
return toFixed(value, nil) + " ms"
} else if absValue < 60000 {
return toFixedScaled(value/1000, nil, " s")
return toFixedScaled(value/1000, " s")
} else if absValue < 3600000 {
return toFixedScaled(value/60000, nil, " min")
return toFixedScaled(value/60000, " min")
} else if absValue < 86400000 { // 172800000 ms is better represented as 2 day
return toFixedScaled(value/3600000, nil, " hour")
return toFixedScaled(value/3600000, " hour")
} else if absValue < 31536000000 {
return toFixedScaled(value/86400000, nil, " day")
return toFixedScaled(value/86400000, " day")
}
return toFixedScaled(value/31536000000, nil, " year")
return toFixedScaled(value/31536000000, " year")
}
// toSeconds returns a easy to read string representation of the given value in seconds
@@ -97,24 +97,24 @@ func toSeconds(value float64) string {
absValue := math.Abs(value)
if absValue < 0.000001 {
return toFixedScaled(value*1e9, nil, " ns")
return toFixedScaled(value*1e9, " ns")
} else if absValue < 0.001 {
return toFixedScaled(value*1e6, nil, " µs")
return toFixedScaled(value*1e6, " µs")
} else if absValue < 1 {
return toFixedScaled(value*1e3, nil, " ms")
return toFixedScaled(value*1e3, " ms")
} else if absValue < 60 {
return toFixed(value, nil) + " s"
} else if absValue < 3600 {
return toFixedScaled(value/60, nil, " min")
return toFixedScaled(value/60, " min")
} else if absValue < 86400 { // 56000 s is better represented as 15.56 hour
return toFixedScaled(value/3600, nil, " hour")
return toFixedScaled(value/3600, " hour")
} else if absValue < 604800 {
return toFixedScaled(value/86400, nil, " day")
return toFixedScaled(value/86400, " day")
} else if absValue < 31536000 {
return toFixedScaled(value/604800, nil, " week")
return toFixedScaled(value/604800, " week")
}
return toFixedScaled(value/3.15569e7, nil, " year")
return toFixedScaled(value/3.15569e7, " year")
}
// toMinutes returns a easy to read string representation of the given value in minutes
@@ -124,13 +124,13 @@ func toMinutes(value float64) string {
if absValue < 60 {
return toFixed(value, nil) + " min"
} else if absValue < 1440 {
return toFixedScaled(value/60, nil, " hour")
return toFixedScaled(value/60, " hour")
} else if absValue < 10080 {
return toFixedScaled(value/1440, nil, " day")
return toFixedScaled(value/1440, " day")
} else if absValue < 604800 {
return toFixedScaled(value/10080, nil, " week")
return toFixedScaled(value/10080, " week")
} else {
return toFixedScaled(value/5.25948e5, nil, " year")
return toFixedScaled(value/5.25948e5, " year")
}
}
@@ -142,11 +142,11 @@ func toHours(value float64) string {
if absValue < 24 {
return toFixed(value, nil) + " hour"
} else if absValue < 168 {
return toFixedScaled(value/24, nil, " day")
return toFixedScaled(value/24, " day")
} else if absValue < 8760 {
return toFixedScaled(value/168, nil, " week")
return toFixedScaled(value/168, " week")
} else {
return toFixedScaled(value/8760, nil, " year")
return toFixedScaled(value/8760, " year")
}
}
@@ -157,9 +157,9 @@ func toDays(value float64) string {
if absValue < 7 {
return toFixed(value, nil) + " day"
} else if absValue < 365 {
return toFixedScaled(value/7, nil, " week")
return toFixedScaled(value/7, " week")
} else {
return toFixedScaled(value/365, nil, " year")
return toFixedScaled(value/365, " year")
}
}
@@ -170,6 +170,6 @@ func toWeeks(value float64) string {
if absValue < 52 {
return toFixed(value, nil) + " week"
} else {
return toFixedScaled(value/52, nil, " year")
return toFixedScaled(value/52, " year")
}
}

View File

@@ -1,4 +1,4 @@
package formatter
package units
import (
"testing"