Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced logging v1 #4722

Merged
merged 7 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/atlas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
},
"dependencies": {
"@apollo/client": "^3.7.2",
"@elastic/apm-rum": "^5.12.0",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@hcaptcha/react-hcaptcha": "^1.4.4",
Expand Down Expand Up @@ -68,6 +69,7 @@
"immer": "^9.0.16",
"localforage": "^1.10.0",
"lodash-es": "^4.17.21",
"mp4box": "^0.5.2",
"multihashes": "^4.0.3",
"postcss-syntax": "^0.36.2",
"rc-slider": "^10.1.0",
Expand Down
6 changes: 4 additions & 2 deletions packages/atlas/src/components/AssetImage/AssetImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import { CSSTransition, SwitchTransition } from 'react-transition-group'

import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader'
import { useGetAssetUrl } from '@/hooks/useGetAssetUrl'
import { AssetType } from '@/providers/uploads/uploads.types'
import { cVar, transitions } from '@/styles'

export type AssetImageProps = {
isLoading?: boolean
resolvedUrls: string[] | undefined | null
type: AssetType | null
imagePlaceholder?: ReactNode
} & Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'>

export const AssetImage: FC<AssetImageProps> = ({ resolvedUrls, isLoading, imagePlaceholder, ...imgProps }) => {
const { url, isLoading: isResolving } = useGetAssetUrl(resolvedUrls, 'image')
export const AssetImage: FC<AssetImageProps> = ({ resolvedUrls, isLoading, type, imagePlaceholder, ...imgProps }) => {
const { url, isLoading: isResolving } = useGetAssetUrl(resolvedUrls, type)

const loading = isLoading || isResolving

Expand Down
2 changes: 1 addition & 1 deletion packages/atlas/src/components/AssetVideo/AssetVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export type AssetVideoProps = {
export const AssetVideo = forwardRef<HTMLVideoElement, AssetVideoProps>(
({ resolvedVideoUrls, resolvedPosterUrls, ...props }: AssetVideoProps, ref) => {
const { url: videoSrc } = useGetAssetUrl(resolvedVideoUrls, 'video')
const { url: posterSrc } = useGetAssetUrl(resolvedPosterUrls, 'image')
const { url: posterSrc } = useGetAssetUrl(resolvedPosterUrls, 'cover')

return <video {...props} ref={ref} src={videoSrc} poster={posterSrc} />
}
Expand Down
1 change: 1 addition & 0 deletions packages/atlas/src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export const Avatar: FC<AvatarProps> = ({
onError={onError}
isLoading={loading}
imagePlaceholder={<SilhouetteAvatar />}
type="avatar"
/>
))}
{children && (loading ? <StyledSkeletonLoader rounded /> : <ChildrenWrapper>{children}</ChildrenWrapper>)}
Expand Down
7 changes: 5 additions & 2 deletions packages/atlas/src/components/Searchbar/SearchBox/Result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ export const Result: FC<ResultProps> = ({
loading,
}) => {
const title = video ? video.title : channel?.title
const { url: channelAvatar, isLoading: isLoadingAvatar } = useGetAssetUrl(channel?.avatarPhoto?.resolvedUrls, 'image')
const { url: channelAvatar, isLoading: isLoadingAvatar } = useGetAssetUrl(
channel?.avatarPhoto?.resolvedUrls,
'avatar'
)
const { url: videoThumbnail, isLoading: isLoadingThumbnail } = useGetAssetUrl(
video?.thumbnailPhoto?.resolvedUrls,
'image'
'thumbnail'
)
const to = useMemo(() => {
if (video) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const ChannelCover: FC<ChannelCoverProps> = ({
<Media>
<CoverImage
resolvedUrls={assetUrls}
type="cover"
imagePlaceholder={
hasCoverUploadFailed ? (
<FailedUploadContainer>
Expand Down
3 changes: 2 additions & 1 deletion packages/atlas/src/components/_inputs/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ export const ComboBox = <T extends unknown>(props: ComboBoxProps<T>) => {
size="large"
highlight={highlightedIndex === index}
nodeStart={
item.nodeStart || (item.thumbnailUrls && <StyledThumbnail resolvedUrls={item.thumbnailUrls} />)
item.nodeStart ||
(item.thumbnailUrls && <StyledThumbnail resolvedUrls={item.thumbnailUrls} type="thumbnail" />)
}
isSeparator={item.isSeparator}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const SubtitlesBox: FC<SubtitleBoxProps> = ({
const { mutateAsync: subtitlesFetch } = useMutation('subtitles-fetch', (url: string) =>
axiosInstance.get(url, { responseType: 'blob' })
)
const { url } = useGetAssetUrl(asset?.resolvedUrls, 'subtitle')
const { url } = useGetAssetUrl(asset?.resolvedUrls, 'subtitles')

const handleDownload = async (url = '') => {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics'
import { hapiBnToTokenNumber, tokenNumberToHapiBn } from '@/joystream-lib/utils'
import { useFee, useJoystream, useTokenPrice } from '@/providers/joystream'
import { useTransaction } from '@/providers/transactions/transactions.hooks'
import { UserEventsLogger } from '@/utils/logs'
import { formatNumber } from '@/utils/number'
import { useChannelPaymentsHistory } from '@/views/studio/MyPaymentsView/PaymentsTransactions/PaymentTransactions.hooks'

Expand Down Expand Up @@ -95,6 +96,7 @@ export const WithdrawFundsDialog: FC<WithdrawFundsDialogProps> = ({
),
onTxSync: async () => {
fetchPaymentsData()
UserEventsLogger.logFundsWithdrawal(channelId, formatNumber(data.amount || 0))
trackWithdrawnFunds(channelId, formatNumber(data.amount || 0))
onExitClick()
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const BackgroundVideoPlayer: FC<BackgroundVideoPlayerProps> = ({
classNames={transitions.names.fade}
timeout={300}
>
<VideoPoster resolvedUrls={poster} alt="" />
<VideoPoster resolvedUrls={poster} type="cover" alt="" />
</CSSTransition>
)}
</StyledLink>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const EndingOverlay: FC<EndingOverlayProps> = ({
navigate(absoluteRoutes.viewer.video(randomNextVideo.id))
}}
>
<VideoThumbnail resolvedUrls={thumbnailUrls} />
<VideoThumbnail resolvedUrls={thumbnailUrls} type="video" />
<VideoInfo>
<Text as="span" variant={mdMatch ? 't300' : 't200'} color="colorText">
Up next
Expand Down
21 changes: 19 additions & 2 deletions packages/atlas/src/components/_video/VideoPlayer/VideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { useMediaMatch } from '@/hooks/useMediaMatch'
import { useSegmentAnalytics, videoPlaybackParams } from '@/hooks/useSegmentAnalytics'
import { usePersonalDataStore } from '@/providers/personalData'
import { isMobile } from '@/utils/browser'
import { ConsoleLogger, SentryLogger } from '@/utils/logs'
import { ConsoleLogger, SentryLogger, UserEventsLogger } from '@/utils/logs'
import { formatDurationShort } from '@/utils/time'

import { ControlsIndicator } from './ControlsIndicator'
Expand Down Expand Up @@ -160,6 +160,8 @@ const VideoPlayerComponent: ForwardRefRenderFunction<HTMLVideoElement, VideoPlay
const [isFullScreen, setIsFullScreen] = useState(false)
const [isPiPEnabled, setIsPiPEnabled] = useState(false)
const [isSettingsPopoverOpened, setIsSettingsPopoverOpened] = useState(false)
const [waitingStateCount, setWaitingStateCount] = useState(0)
const [slowNetworkEventSent, setSlowNetworkEventSent] = useState(false)
attemka marked this conversation as resolved.
Show resolved Hide resolved

const [autoplayEventFired, setAutoplayEventFired] = useState(!(typeof autoplay === 'boolean'))
const [playerState, setPlayerState] = useState<PlayerState>('loading')
Expand Down Expand Up @@ -224,6 +226,9 @@ const VideoPlayerComponent: ForwardRefRenderFunction<HTMLVideoElement, VideoPlay
if (error.name === 'NotAllowedError') {
ConsoleLogger.warn('Video playback failed', error)
} else {
UserEventsLogger.logUserError('playback-has-crashed', {
video: { id: videoId, urls: videoJsConfig.videoUrls },
})
SentryLogger.error('Video playback failed', 'VideoPlayer', error, {
video: { id: videoId, urls: videoJsConfig.videoUrls },
})
Expand Down Expand Up @@ -343,6 +348,18 @@ const VideoPlayerComponent: ForwardRefRenderFunction<HTMLVideoElement, VideoPlay
}
const handler = (event: Event) => {
if (event.type === 'waiting' || event.type === 'seeking') {
if (event.type === 'waiting') {
setWaitingStateCount((prev) => prev + 1)
if (waitingStateCount > 3 && !slowNetworkEventSent) {
UserEventsLogger.logPlaybackIsSlowEvent({
...videoPlaybackData,
dataObjectId: video?.media?.id ?? '',
dataObjectType: 'video',
resolvedUrl: player.src() ?? '',
})
setSlowNetworkEventSent(true)
}
}
setPlayerState('loading')
}
if (event.type === 'canplay' || event.type === 'seeked') {
Expand All @@ -353,7 +370,7 @@ const VideoPlayerComponent: ForwardRefRenderFunction<HTMLVideoElement, VideoPlay
return () => {
player.off(['waiting', 'canplay', 'seeking', 'seeked'], handler)
}
}, [player, playerState])
}, [player, playerState, slowNetworkEventSent, video?.media?.id, videoPlaybackData, waitingStateCount])

useEffect(() => {
if (!player) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const useVideoJsPlayer: VideoJsPlayerHook = ({
const playerRef = useRef<HTMLVideoElement | null>(null)
const [player, setPlayer] = useState<VideoJsPlayer | null>(null)
const { url: src } = useGetAssetUrl(videoUrls, 'video')
const { url: posterUrl } = useGetAssetUrl(posterUrls, 'image')
const { url: posterUrl } = useGetAssetUrl(posterUrls, 'cover')
useEffect(() => {
if (!playerRef.current) {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const VideoThumbnail = forwardRef<HTMLAnchorElement, VideoThumbnailProps>
<ThumbnailImage
isLoading={loading}
resolvedUrls={thumbnailUrls}
type="thumbnail"
alt={thumbnailAlt || ''}
imagePlaceholder={<ThumbnailBackground />}
/>
Expand Down
4 changes: 2 additions & 2 deletions packages/atlas/src/embedded/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { createRoot } from 'react-dom/client'

import { atlasConfig } from '@/config'
import { BUILD_ENV } from '@/config/env'
import { AssetLogger, SentryLogger } from '@/utils/logs'
import { SentryLogger, UserEventsLogger } from '@/utils/logs'

import { App } from './App'

const initApp = async () => {
if (BUILD_ENV === 'production') {
SentryLogger.initialize(atlasConfig.analytics.sentry?.dsn)
AssetLogger.initialize(atlasConfig.analytics.assetLogs?.url)
UserEventsLogger.initialize(atlasConfig.analytics.assetLogs?.url)
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Expand Down
3 changes: 2 additions & 1 deletion packages/atlas/src/hooks/useCreateMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { useSnackbar } from '@/providers/snackbars'
import { useTransactionManagerStore } from '@/providers/transactions/transactions.store'
import { useYppStore } from '@/providers/ypp/ypp.store'
import { UploadAvatarServiceError, uploadAvatarImage } from '@/utils/image'
import { ConsoleLogger, SentryLogger } from '@/utils/logs'
import { ConsoleLogger, SentryLogger, UserEventsLogger } from '@/utils/logs'

export type MemberFormData = {
handle: string
Expand Down Expand Up @@ -137,6 +137,7 @@ export const useCreateMember = () => {
iconType: 'error',
})
onError?.(FaucetError.UploadAvatarServiceError)
UserEventsLogger.logUserError('failed-avatar-upload', { message: error.message })
SentryLogger.error('Failed to upload member avatar', 'SignUpModal', error)
return
}
Expand Down
36 changes: 27 additions & 9 deletions packages/atlas/src/hooks/useGetAssetUrl.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import { useEffect, useState } from 'react'

import { atlasConfig } from '@/config'
import { testAssetDownload } from '@/providers/assets/assets.helpers'
import { logDistributorPerformance, testAssetDownload } from '@/providers/assets/assets.helpers'
import { useOperatorsContext } from '@/providers/assets/assets.provider'
import { ConsoleLogger } from '@/utils/logs'
import { AssetType } from '@/providers/uploads/uploads.types'
import { getVideoCodec } from '@/utils/getVideoCodec'
import { ConsoleLogger, DistributorEventEntry, SentryLogger, UserEventsLogger } from '@/utils/logs'
import { withTimeout } from '@/utils/misc'

export const getSingleAssetUrl = async (
urls: string[] | undefined | null,
type: 'image' | 'video' | 'subtitle',
urls: string[] | null | undefined,
id: string | null | undefined,
type: AssetType | null,
timeout?: number
): Promise<string | undefined> => {
if (!urls || !urls.length) {
return
}

for (const distributionAssetUrl of urls) {
const eventEntry: DistributorEventEntry = {
dataObjectId: id,
dataObjectType: type || undefined,
resolvedUrl: distributionAssetUrl,
}

const assetTestPromise = testAssetDownload(distributionAssetUrl, type)
const assetTestPromiseWithTimeout = withTimeout(
assetTestPromise,
Expand All @@ -25,9 +34,17 @@ export const getSingleAssetUrl = async (
try {
await assetTestPromiseWithTimeout

logDistributorPerformance(distributionAssetUrl, eventEntry)

return distributionAssetUrl
} catch {
/**/
} catch (err) {
if (err instanceof MediaError) {
const codec = getVideoCodec(distributionAssetUrl)
UserEventsLogger.logWrongCodecEvent(eventEntry, { codec })
SentryLogger.error('Error during asset download test, media is not supported', 'AssetsManager', err, {
asset: { parent, distributionAssetUrl, mediaError: err, codec },
})
}
}
}

Expand All @@ -50,10 +67,11 @@ export const getSingleAssetUrl = async (
})
}

export const useGetAssetUrl = (urls: string[] | undefined | null, type: 'image' | 'video' | 'subtitle') => {
export const useGetAssetUrl = (urls: string[] | undefined | null, type: AssetType | null) => {
const [url, setUrl] = useState<string | undefined>(undefined)
const [isLoading, setIsLoading] = useState(true)
const { userBenchmarkTime } = useOperatorsContext()
const id = url?.split('/').pop()
useEffect(() => {
if (!urls || (url && urls.includes(url)) || (!url && !urls.length)) {
setIsLoading(false)
Expand All @@ -62,7 +80,7 @@ export const useGetAssetUrl = (urls: string[] | undefined | null, type: 'image'
const init = async () => {
setUrl(undefined)
setIsLoading(true)
const resolvedUrl = await getSingleAssetUrl(urls, type, userBenchmarkTime ?? undefined)
const resolvedUrl = await getSingleAssetUrl(urls, id, type, userBenchmarkTime ?? undefined)
setIsLoading(false)
if (resolvedUrl) {
setUrl(resolvedUrl)
Expand All @@ -74,7 +92,7 @@ export const useGetAssetUrl = (urls: string[] | undefined | null, type: 'image'
return () => {
setIsLoading(false)
}
}, [type, url, urls, userBenchmarkTime])
}, [id, type, url, urls, userBenchmarkTime])

return { url, isLoading }
}
4 changes: 2 additions & 2 deletions packages/atlas/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { createRoot } from 'react-dom/client'

import { atlasConfig } from '@/config'
import { BUILD_ENV } from '@/config/env'
import { AssetLogger, SentryLogger } from '@/utils/logs'
import { SentryLogger, UserEventsLogger } from '@/utils/logs'

import { App } from './App'

const initApp = async () => {
if (BUILD_ENV === 'production') {
SentryLogger.initialize(atlasConfig.analytics.sentry?.dsn)
AssetLogger.initialize(atlasConfig.analytics.assetLogs?.url)
}
UserEventsLogger.initialize(atlasConfig.analytics.assetLogs?.url)

if (typeof globalThis !== 'undefined') {
globalThis.Buffer = Buffer
Expand Down
Loading
Loading