diff --git a/frontend/__snapshots__/components-properties-table--basic--dark.png b/frontend/__snapshots__/components-properties-table--basic--dark.png
index 3890636e34f8a..a561e6ed86b77 100644
Binary files a/frontend/__snapshots__/components-properties-table--basic--dark.png and b/frontend/__snapshots__/components-properties-table--basic--dark.png differ
diff --git a/frontend/__snapshots__/components-properties-table--basic--light.png b/frontend/__snapshots__/components-properties-table--basic--light.png
index de0f90740eb37..43498d9e17326 100644
Binary files a/frontend/__snapshots__/components-properties-table--basic--light.png and b/frontend/__snapshots__/components-properties-table--basic--light.png differ
diff --git a/frontend/__snapshots__/components-properties-table--dollar-properties-on-event--dark.png b/frontend/__snapshots__/components-properties-table--dollar-properties-on-event--dark.png
index fdec449544189..80868ba79c924 100644
Binary files a/frontend/__snapshots__/components-properties-table--dollar-properties-on-event--dark.png and b/frontend/__snapshots__/components-properties-table--dollar-properties-on-event--dark.png differ
diff --git a/frontend/__snapshots__/components-properties-table--dollar-properties-on-event--light.png b/frontend/__snapshots__/components-properties-table--dollar-properties-on-event--light.png
index 4208f3bc30e2e..37d723ef28b8c 100644
Binary files a/frontend/__snapshots__/components-properties-table--dollar-properties-on-event--light.png and b/frontend/__snapshots__/components-properties-table--dollar-properties-on-event--light.png differ
diff --git a/frontend/__snapshots__/components-properties-table--dollar-properties-on-person--dark.png b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person--dark.png
index e6488189d84e4..d13cdb0246334 100644
Binary files a/frontend/__snapshots__/components-properties-table--dollar-properties-on-person--dark.png and b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person--dark.png differ
diff --git a/frontend/__snapshots__/components-properties-table--dollar-properties-on-person--light.png b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person--light.png
index 84d14a856b752..d1064c7d548bf 100644
Binary files a/frontend/__snapshots__/components-properties-table--dollar-properties-on-person--light.png and b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person--light.png differ
diff --git a/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx b/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx
index daa07771ac1df..b245d5db8bae4 100644
--- a/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx
+++ b/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx
@@ -8,10 +8,16 @@ import { combineUrl } from 'kea-router'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonTable, LemonTableColumns, LemonTableProps } from 'lib/lemon-ui/LemonTable'
import { userPreferencesLogic } from 'lib/logic/userPreferencesLogic'
-import { CORE_FILTER_DEFINITIONS_BY_GROUP, PROPERTY_KEYS } from 'lib/taxonomy'
+import {
+ CORE_FILTER_DEFINITIONS_BY_GROUP,
+ getCoreFilterDefinition,
+ NON_DOLLAR_POSTHOG_PROPERTY_KEYS,
+ PROPERTY_KEYS,
+} from 'lib/taxonomy'
import { isURL } from 'lib/utils'
import { useMemo, useState } from 'react'
import { NewProperty } from 'scenes/persons/NewProperty'
+import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { urls } from 'scenes/urls'
import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel'
@@ -209,12 +215,26 @@ export function PropertiesTable({
const [searchTerm, setSearchTerm] = useState('')
const { hidePostHogPropertiesInTable } = useValues(userPreferencesLogic)
const { setHidePostHogPropertiesInTable } = useActions(userPreferencesLogic)
+ const { isCloudOrDev } = useValues(preflightLogic)
const objectProperties = useMemo(() => {
if (!properties || Array.isArray(properties)) {
return []
}
- let entries = Object.entries(properties)
+ let entries = Object.entries(properties).sort((a, b) => {
+ // if this is a posthog property we want to sort by its label
+ const left = getCoreFilterDefinition(a[0], TaxonomicFilterGroupType.EventProperties)?.label || a[0]
+ const right = getCoreFilterDefinition(b[0], TaxonomicFilterGroupType.EventProperties)?.label || b[0]
+
+ if (left < right) {
+ return -1
+ }
+ if (left > right) {
+ return 1
+ }
+ return 0
+ })
+
if (searchTerm) {
const normalizedSearchTerm = searchTerm.toLowerCase()
entries = entries.filter(([key, value]) => {
@@ -228,7 +248,11 @@ export function PropertiesTable({
}
if (filterable && hidePostHogPropertiesInTable) {
- entries = entries.filter(([key]) => !key.startsWith('$') && !PROPERTY_KEYS.includes(key))
+ entries = entries.filter(([key]) => {
+ const isPostHogProperty = key.startsWith('$') && PROPERTY_KEYS.includes(key)
+ const isNonDollarPostHogProperty = isCloudOrDev && NON_DOLLAR_POSTHOG_PROPERTY_KEYS.includes(key)
+ return !isPostHogProperty && !isNonDollarPostHogProperty
+ })
}
if (sortProperties) {
diff --git a/frontend/src/lib/taxonomy.tsx b/frontend/src/lib/taxonomy.tsx
index 75b55ca7f705c..b8a333f2d2fa8 100644
--- a/frontend/src/lib/taxonomy.tsx
+++ b/frontend/src/lib/taxonomy.tsx
@@ -249,6 +249,80 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = {
description: 'If autocapture has been disabled server-side.',
system: true,
},
+ $feature_flag_payloads: {
+ label: 'Feature Flag Payloads',
+ description: 'Feature flag payloads active in the environment.',
+ },
+ $capture_failed_request: {
+ label: 'Capture Failed Request',
+ description: '',
+ },
+ $lib_rate_limit_remaining_tokens: {
+ label: 'Clientside rate limit remaining tokens',
+ description: (
+
+ Remaining rate limit tokens for the posthog-js library client-side rate limiting implementation.
+
+ ),
+ examples: ['100'],
+ },
+ token: {
+ label: 'Token',
+ description: Token used for authentication.,
+ examples: ['ph_abcdefg'],
+ },
+ $ce_version: {
+ label: '$ce_version',
+ description: '',
+ system: true,
+ },
+ $anon_distinct_id: {
+ label: 'Anon Distinct ID',
+ description: 'If the user was previously anonymous, their anonymous ID will be set here.',
+ examples: ['16ff262c4301e5-0aa346c03894bc-39667c0e-1aeaa0-16ff262c431767'],
+ system: true,
+ },
+ $event_type: {
+ label: 'Event Type',
+ description:
+ 'When the event is an $autocapture event, this specifies what the action was against the element.',
+ examples: ['click', 'submit', 'change'],
+ },
+ $insert_id: {
+ label: 'Insert ID',
+ description: 'Unique insert ID for the event.',
+ system: true,
+ },
+ $time: {
+ label: '$time (deprecated)',
+ description:
+ 'Use the HogQL field `timestamp` instead. This field was previously set on some client side events.',
+ system: true,
+ examples: ['1681211521.345'],
+ },
+ $device_id: {
+ label: 'Device ID',
+ description: 'Unique ID for that device, consistent even if users are logging in/out.',
+ examples: ['16ff262c4301e5-0aa346c03894bc-39667c0e-1aeaa0-16ff262c431767'],
+ system: true,
+ },
+ $browser_type: {
+ label: 'Browser Type',
+ description: 'This is only added when posthog-js config.opt_out_useragent_filter is true.',
+ examples: ['browser', 'bot'],
+ },
+
+ // session recording
+ $replay_minimum_duration: {
+ label: 'Replay config - minimum duration',
+ description: Config for minimum duration before emitting a session recording.,
+ examples: ['1000'],
+ },
+ $replay_sample_rate: {
+ label: 'Replay config - sample rate',
+ description: Config for sampling rate of session recordings.,
+ examples: ['0.1'],
+ },
$console_log_recording_enabled_server_side: {
label: 'Console Log Recording Enabled Server-Side',
description: 'If console log recording has been enabled server-side.',
@@ -260,14 +334,44 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = {
examples: ['v2'],
system: true,
},
- $feature_flag_payloads: {
- label: 'Feature Flag Payloads',
- description: 'Feature flag payloads active in the environment.',
+ $session_recording_start_reason: {
+ label: 'Session recording start reason',
+ description: (
+
+ Reason for starting the session recording. Useful for e.g. if you have sampling enabled and want to
+ see on batch exported events which sessions have recordings available.
+
+ ),
+ examples: ['sampling_override', 'recording_initialized', 'linked_flag_match'],
},
- $capture_failed_request: {
- label: 'Capture Failed Request',
- description: '',
+ $session_recording_canvas_recording: {
+ label: 'Session recording canvas recording',
+ description: Session recording canvas capture config.,
+ examples: ['{"enabled": false}'],
},
+ $session_recording_network_payload_capture: {
+ label: 'Session recording network payload capture',
+ description: Session recording network payload capture config.,
+ examples: ['{"recordHeaders": false}'],
+ },
+ $session_recording_url_trigger_activated_session: {
+ label: 'Session recording URL trigger activated session',
+ description: (
+
+ Session recording URL trigger activated session config. Used by posthog-js to track URL activation
+ of session replay.
+
+ ),
+ },
+ $session_recording_url_trigger_status: {
+ label: 'Session recording URL trigger status',
+ description: (
+
+ Session recording URL trigger status. Used by posthog-js to track URL activation of session replay.
+
+ ),
+ },
+ // exception tracking
$sentry_exception: {
label: 'Sentry exception',
description: 'Raw Sentry exception data',
@@ -324,41 +428,21 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = {
label: 'Exception person URL',
description: 'The PostHog person that experienced the exception',
},
- $ce_version: {
- label: '$ce_version',
- description: '',
- system: true,
- },
- $anon_distinct_id: {
- label: 'Anon Distinct ID',
- description: 'If the user was previously anonymous, their anonymous ID will be set here.',
- examples: ['16ff262c4301e5-0aa346c03894bc-39667c0e-1aeaa0-16ff262c431767'],
- system: true,
- },
- $event_type: {
- label: 'Event Type',
- description:
- 'When the event is an $autocapture event, this specifies what the action was against the element.',
- examples: ['click', 'submit', 'change'],
- },
- $insert_id: {
- label: 'Insert ID',
- description: 'Unique insert ID for the event.',
- system: true,
+ $exception_capture_endpoint: {
+ label: 'Exception capture endpoint',
+ description: Endpoint used by posthog-js exception autocapture.,
+ examples: ['/e/'],
},
- $time: {
- label: '$time (deprecated)',
- description:
- 'Use the HogQL field `timestamp` instead. This field was previously set on some client side events.',
- system: true,
- examples: ['1681211521.345'],
+ $exception_capture_endpoint_suffix: {
+ label: 'Exception capture endpoint',
+ description: Endpoint used by posthog-js exception autocapture.,
+ examples: ['/e/'],
},
- $device_id: {
- label: 'Device ID',
- description: 'Unique ID for that device, consistent even if users are logging in/out.',
- examples: ['16ff262c4301e5-0aa346c03894bc-39667c0e-1aeaa0-16ff262c431767'],
- system: true,
+ $exception_capture_enabled_server_side: {
+ label: 'Exception capture enabled server side',
+ description: Whether exception autocapture was enabled in remote config.,
},
+
// GeoIP
$geoip_city_name: {
label: 'City Name',
@@ -435,6 +519,27 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = {
label: 'GeoIP Disabled',
description: `Whether to skip GeoIP processing for the event.`,
},
+ $geoip_city_confidence: {
+ label: 'GeoIP detection city confidence',
+ description: "Confidence level of the city matched to this event's IP address.",
+ examples: ['0.5'],
+ },
+ $geoip_country_confidence: {
+ label: 'GeoIP detection country confidence',
+ description: "Confidence level of the country matched to this event's IP address.",
+ examples: ['0.5'],
+ },
+ $geoip_accuracy_radius: {
+ label: 'GeoIP detection accuracy radius',
+ description: "Accuracy radius of the location matched to this event's IP address.",
+ examples: ['50'],
+ },
+ $geoip_subdivision_1_confidence: {
+ label: 'GeoIP detection subdivision 1 confidence',
+ description: "Confidence level of the first subdivision matched to this event's IP address.",
+ examples: ['0.5'],
+ },
+
$el_text: {
label: 'Element Text',
description: `The text of the element that was clicked. Only sent with Autocapture events.`,
@@ -1017,7 +1122,10 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = {
label: 'Is Identified',
description: 'When the person was identified',
},
-
+ $initial_person_info: {
+ label: 'Initial Person Info',
+ description: 'posthog-js initial person information. used in the $set_once flow',
+ },
// web vitals properties
$web_vitals_enabled_server_side: {
label: 'Web vitals enabled server side',
@@ -1047,6 +1155,63 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = {
$web_vitals_CLS_value: {
label: 'Web vitals CLS value',
},
+ $web_vitals_allowed_metrics: {
+ label: 'Web vitals allowed metrics',
+ description: Allowed web vitals metrics config.,
+ examples: ['["LCP", "CLS"]'],
+ },
+
+ // page leave properties
+ $prev_pageview_last_scroll: {
+ label: 'Previous pageview last scroll',
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ examples: [0],
+ },
+ $prev_pageview_last_scroll_percentage: {
+ label: 'Previous pageview last scroll percentage',
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ examples: [0],
+ },
+ $prev_pageview_max_scroll: {
+ examples: [0],
+ label: 'Previous pageview max scroll',
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ },
+ $prev_pageview_max_scroll_percentage: {
+ examples: [0],
+ label: 'Previous pageview max scroll percentage',
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ },
+ $prev_pageview_last_content: {
+ examples: [0],
+ label: 'Previous pageview last content',
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ },
+ $prev_pageview_last_content_percentage: {
+ examples: [0],
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ label: 'Previous pageview last content percentage',
+ },
+ $prev_pageview_max_content: {
+ examples: [0],
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ label: 'Previous pageview max content',
+ },
+ $prev_pageview_max_content_percentage: {
+ examples: [0],
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ label: 'Previous pageview max content percentage',
+ },
+ $prev_pageview_pathname: {
+ examples: ['/pricing', '/about-us/team'],
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ label: 'Previous pageview pathname',
+ },
+ $prev_pageview_duration: {
+ examples: [0],
+ description: 'posthog-js adds these to the page leave event, they are used in web analytics calculations',
+ label: 'Previous pageview duration',
+ },
},
numerical_event_properties: {}, // Same as event properties, see assignment below
person_properties: {}, // Currently person properties are the same as event properties, see assignment below
@@ -1143,89 +1308,6 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = {
),
examples: ['2.2'],
},
- $session_recording_start_reason: {
- label: 'Session recording start reason',
- description: (
-
- Reason for starting the session recording. Useful for e.g. if you have sampling enabled and want to
- see on batch exported events which sessions have recordings available.
-
- ),
- examples: ['sampling_override', 'recording_initialized', 'linked_flag_match'],
- },
- $replay_minimum_duration: {
- label: 'Replay config - minimum duration',
- description: Config for minimum duration before emitting a session recording.,
- examples: ['1000'],
- },
- $replay_sample_rate: {
- label: 'Replay config - sample rate',
- description: Config for sampling rate of session recordings.,
- examples: ['0.1'],
- },
- $exception_capture_endpoint: {
- label: 'Exception capture endpoint',
- description: Endpoint used by posthog-js exception autocapture.,
- examples: ['/e/'],
- },
- $exception_capture_endpoint_suffix: {
- label: 'Exception capture endpoint',
- description: Endpoint used by posthog-js exception autocapture.,
- examples: ['/e/'],
- },
- $exception_capture_enabled_server_side: {
- label: 'Exception capture enabled server side',
- description: Whether exception autocapture was enabled in remote config.,
- },
- $lib_rate_limit_remaining_tokens: {
- label: 'Clientside rate limit remaining tokens',
- description: (
-
- Remaining rate limit tokens for the posthog-js library client-side rate limiting implementation.
-
- ),
- examples: ['100'],
- },
- $session_recording_canvas_recording: {
- label: 'Session recording canvas recording',
- description: Session recording canvas capture config.,
- examples: ['{"enabled": false}'],
- },
- $session_recording_network_payload_capture: {
- label: 'Session recording network payload capture',
- description: Session recording network payload capture config.,
- examples: ['{"recordHeaders": false}'],
- },
- token: {
- label: 'Token',
- description: Token used for authentication.,
- examples: ['ph_abcdefg'],
- },
- $web_vitals_allowed_metrics: {
- label: 'Web vitals allowed metrics',
- description: Allowed web vitals metrics config.,
- examples: ['["LCP", "CLS"]'],
- },
- $geoip_city_confidence: {
- label: 'GeoIP detection city confidence',
- description: "Confidence level of the city matched to this event's IP address.",
- examples: ['0.5'],
- },
- $geoip_country_confidence: {
- label: 'GeoIP detection country confidence',
- description: "Confidence level of the country matched to this event's IP address.",
- examples: ['0.5'],
- },
- $geoip_accuracy_radius: {
- label: 'GeoIP detection accuracy radius',
- description: "Accuracy radius of the location matched to this event's IP address.",
- examples: ['50'],
- },
- $geoip_subdivision_1_confidence: {
- label: 'GeoIP detection subdivision 1 confidence',
- description: "Confidence level of the first subdivision matched to this event's IP address.",
- examples: ['0.5'],
- },
},
groups: {
$group_key: {
@@ -1325,6 +1407,77 @@ CORE_FILTER_DEFINITIONS_BY_GROUP.event_properties.$session_duration =
export const PROPERTY_KEYS = Object.keys(CORE_FILTER_DEFINITIONS_BY_GROUP.event_properties)
+/**
+ * these are properties that PostHog add to events they track for their own purposes
+ * not part of the general taxonomy
+ * but often more numerous than actual properties set on events and useful to hide
+ * to make those properties discoverable
+ */
+export const NON_DOLLAR_POSTHOG_PROPERTY_KEYS = [
+ 'billing_period_end',
+ 'billing_period_start',
+ 'current_amount_usd.data_warehouse',
+ 'current_amount_usd.feature_flags',
+ 'current_amount_usd.integrations',
+ 'current_amount_usd.platform_and_support',
+ 'current_amount_usd.product_analytics',
+ 'current_amount_usd.session_replay',
+ 'current_amount_usd.surveys',
+ 'current_total_amount_usd',
+ 'current_usage.data_warehouse',
+ 'current_usage.feature_flags',
+ 'current_usage.integrations',
+ 'current_usage.platform_and_support',
+ 'current_usage.product_analytics',
+ 'current_usage.session_replay',
+ 'current_usage.surveys',
+ 'customer_deactivated',
+ 'custom_limits_usd.data_warehouse',
+ 'free_allocation.data_warehouse',
+ 'free_allocation.feature_flags',
+ 'free_allocation.integrations',
+ 'free_allocation.platform_and_support',
+ 'free_allocation.product_analytics',
+ 'free_allocation.session_replay',
+ 'free_allocation.surveys',
+ 'has_billing_plan',
+ 'percentage_usage.data_warehouse',
+ 'percentage_usage.feature_flags',
+ 'percentage_usage.integrations',
+ 'percentage_usage.platform_and_support',
+ 'percentage_usage.product_analytics',
+ 'percentage_usage.session_replay',
+ 'percentage_usage.surveys',
+ 'projected_usage.data_warehouse',
+ 'projected_usage.feature_flags',
+ 'projected_usage.integrations',
+ 'projected_usage.platform_and_support',
+ 'projected_usage.product_analytics',
+ 'projected_usage.session_replay',
+ 'projected_usage.surveys',
+ 'unit_amount_usd.data_warehouse',
+ 'unit_amount_usd.feature_flags',
+ 'unit_amount_usd.integrations',
+ 'unit_amount_usd.platform_and_support',
+ 'unit_amount_usd.product_analytics',
+ 'unit_amount_usd.session_replay',
+ 'unit_amount_usd.surveys',
+ 'usage_limit.data_warehouse',
+ 'usage_limit.feature_flags',
+ 'usage_limit.integrations',
+ 'usage_limit.platform_and_support',
+ 'usage_limit.product_analytics',
+ 'usage_limit.session_replay',
+ 'usage_limit.surveys',
+ 'is_demo_project',
+ 'realm',
+ 'email_service_available',
+ 'slack_service_available',
+ 'commit_sha',
+ 'token',
+ 'distinct_id',
+]
+
/** Return whether a given filter key is part of PostHog's core (marked by the PostHog logo). */
export function isCoreFilter(key: string): boolean {
return Object.values(CORE_FILTER_DEFINITIONS_BY_GROUP).some((mapping) => Object.keys(mapping).includes(key))
diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx
index 9dd41215f9190..c56eeaf3a9683 100644
--- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx
+++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx
@@ -5,6 +5,7 @@ import { FEATURE_FLAGS } from 'lib/constants'
import { IconUnverifiedEvent } from 'lib/lemon-ui/icons'
import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import { userPreferencesLogic } from 'lib/logic/userPreferencesLogic'
import { capitalizeFirstLetter } from 'lib/utils'
import { IconWindow } from 'scenes/session-recordings/player/icons'
@@ -19,6 +20,81 @@ import {
import { InspectorSearchInfo } from './components/InspectorSearchInfo'
import { playerInspectorLogic } from './playerInspectorLogic'
+function HideProperties(): JSX.Element | null {
+ const { logicProps } = useValues(sessionRecordingPlayerLogic)
+ const inspectorLogic = playerInspectorLogic(logicProps)
+ const { tab } = useValues(inspectorLogic)
+ const { hidePostHogPropertiesInTable } = useValues(userPreferencesLogic)
+ const { setHidePostHogPropertiesInTable } = useActions(userPreferencesLogic)
+
+ return tab === SessionRecordingPlayerTab.EVENTS ? (
+