Skip to content

Commit c5dc5f8

Browse files
authored
Merge pull request #6403 from Shopify/lopert.logs-location
Move app logs to app's `.shopify` directory
2 parents f5a992b + 8f80a4a commit c5dc5f8

File tree

8 files changed

+52
-48
lines changed

8 files changed

+52
-48
lines changed

packages/app/src/cli/models/app/app.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ export interface AppInterface<
318318
updateHiddenConfig: (values: Partial<AppHiddenConfig>) => Promise<void>
319319
setDevApplicationURLs: (devApplicationURLs: ApplicationURLs) => void
320320
generateExtensionTypes(): Promise<void>
321+
getLogsDir(): string
321322
}
322323

323324
type AppConstructor<
@@ -445,6 +446,10 @@ export class App<
445446
return this._hiddenConfig
446447
}
447448

449+
getLogsDir() {
450+
return joinPath(this.directory, '.shopify', 'logs')
451+
}
452+
448453
async updateHiddenConfig(values: Partial<AppHiddenConfig>) {
449454
if (!this.configuration.client_id) return
450455
this._hiddenConfig = deepMergeObjects(this.hiddenConfig, values)

packages/app/src/cli/services/app-logs/dev/poll-app-logs.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import camelcaseKeys from 'camelcase-keys'
1010

1111
const JWT_TOKEN = 'jwtToken'
1212
const API_KEY = 'apiKey'
13+
const TEST_LOGS_DIR = '/test/logs/dir'
1314

1415
vi.mock('./write-app-logs.js')
1516
vi.mock('@shopify/cli-kit/node/http')
@@ -247,11 +248,11 @@ describe('pollAppLogs', () => {
247248
await pollAppLogs({
248249
stdout,
249250
appLogsFetchInput: {jwtToken: JWT_TOKEN},
250-
apiKey: API_KEY,
251251
developerPlatformClient,
252252
resubscribeCallback: MOCKED_RESUBSCRIBE_CALLBACK,
253253
storeName: 'storeName',
254254
organizationId: 'organizationId',
255+
logsDir: TEST_LOGS_DIR,
255256
})
256257
await vi.advanceTimersToNextTimerAsync()
257258

@@ -261,9 +262,9 @@ describe('pollAppLogs', () => {
261262
expect(writeAppLogsToFile).toHaveBeenCalledWith({
262263
appLog: RESPONSE_DATA.app_logs[0],
263264
appLogPayload: appLogPayloadZero,
264-
apiKey: API_KEY,
265265
stdout,
266266
storeName: 'storeName',
267+
logsDir: TEST_LOGS_DIR,
267268
})
268269

269270
const appLogPayloadOne = new FunctionRunLog(
@@ -272,16 +273,16 @@ describe('pollAppLogs', () => {
272273
expect(writeAppLogsToFile).toHaveBeenCalledWith({
273274
appLog: RESPONSE_DATA.app_logs[1],
274275
appLogPayload: appLogPayloadOne,
275-
apiKey: API_KEY,
276276
stdout,
277277
storeName: 'storeName',
278+
logsDir: TEST_LOGS_DIR,
278279
})
279280
expect(writeAppLogsToFile).toHaveBeenCalledWith({
280281
appLog: RESPONSE_DATA.app_logs[2],
281282
appLogPayload: JSON.parse(RESPONSE_DATA.app_logs[2]!.payload),
282-
apiKey: API_KEY,
283283
stdout,
284284
storeName: 'storeName',
285+
logsDir: TEST_LOGS_DIR,
285286
})
286287

287288
expect(components.useConcurrentOutputContext).toHaveBeenCalledWith(
@@ -352,11 +353,11 @@ describe('pollAppLogs', () => {
352353
await pollAppLogs({
353354
stdout,
354355
appLogsFetchInput: {jwtToken: JWT_TOKEN},
355-
apiKey: API_KEY,
356356
developerPlatformClient: mockedDeveloperPlatformClient,
357357
resubscribeCallback: MOCKED_RESUBSCRIBE_CALLBACK,
358358
storeName: 'storeName',
359359
organizationId: 'organizationId',
360+
logsDir: TEST_LOGS_DIR,
360361
})
361362

362363
expect(MOCKED_RESUBSCRIBE_CALLBACK).toHaveBeenCalled()
@@ -374,11 +375,11 @@ describe('pollAppLogs', () => {
374375
await pollAppLogs({
375376
stdout,
376377
appLogsFetchInput: {jwtToken: JWT_TOKEN},
377-
apiKey: API_KEY,
378378
developerPlatformClient: mockedDeveloperPlatformClient,
379379
resubscribeCallback: MOCKED_RESUBSCRIBE_CALLBACK,
380380
storeName: 'storeName',
381381
organizationId: 'organizationId',
382+
logsDir: TEST_LOGS_DIR,
382383
})
383384

384385
expect(outputWarnSpy).toHaveBeenCalledWith('Request throttled while polling app logs.', stdout)
@@ -400,11 +401,11 @@ describe('pollAppLogs', () => {
400401
await pollAppLogs({
401402
stdout,
402403
appLogsFetchInput: {jwtToken: JWT_TOKEN},
403-
apiKey: API_KEY,
404404
developerPlatformClient: mockedDeveloperPlatformClient,
405405
resubscribeCallback: MOCKED_RESUBSCRIBE_CALLBACK,
406406
storeName: 'storeName',
407407
organizationId: 'organizationId',
408+
logsDir: TEST_LOGS_DIR,
408409
})
409410

410411
// Then
@@ -443,11 +444,11 @@ describe('pollAppLogs', () => {
443444
await pollAppLogs({
444445
stdout,
445446
appLogsFetchInput: {jwtToken: JWT_TOKEN},
446-
apiKey: API_KEY,
447447
developerPlatformClient: mockedDeveloperPlatformClient,
448448
resubscribeCallback: MOCKED_RESUBSCRIBE_CALLBACK,
449449
storeName: 'storeName',
450450
organizationId: 'organizationId',
451+
logsDir: TEST_LOGS_DIR,
451452
})
452453

453454
// When/Then

packages/app/src/cli/services/app-logs/dev/poll-app-logs.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,21 @@ import {Writable} from 'stream'
2323
export const pollAppLogs = async ({
2424
stdout,
2525
appLogsFetchInput: {jwtToken, cursor},
26-
apiKey,
2726
developerPlatformClient,
2827
resubscribeCallback,
2928
storeName,
3029
organizationId,
3130
abortSignal,
31+
logsDir,
3232
}: {
3333
stdout: Writable
3434
appLogsFetchInput: AppLogsOptions
35-
apiKey: string
3635
developerPlatformClient: DeveloperPlatformClient
3736
resubscribeCallback: () => Promise<string>
3837
storeName: string
3938
organizationId: string
4039
abortSignal?: AbortSignal
40+
logsDir: string
4141
}) => {
4242
if (abortSignal?.aborted) {
4343
return
@@ -69,9 +69,9 @@ export const pollAppLogs = async ({
6969
const logFile = await writeAppLogsToFile({
7070
appLog: log,
7171
appLogPayload: payload,
72-
apiKey,
7372
stdout,
7473
storeName,
74+
logsDir,
7575
})
7676
stdout.write(
7777
outputContent`${outputToken.gray('└ ')}${outputToken.link(
@@ -117,12 +117,12 @@ export const pollAppLogs = async ({
117117
jwtToken: nextJwtToken,
118118
cursor: responseCursor || cursor,
119119
},
120-
apiKey,
121120
developerPlatformClient,
122121
resubscribeCallback,
123122
storeName,
124123
organizationId,
125124
abortSignal,
125+
logsDir,
126126
}).catch((error) => {
127127
outputDebug(`Unexpected error during polling: ${error}}\n`)
128128
})
@@ -140,12 +140,12 @@ export const pollAppLogs = async ({
140140
jwtToken,
141141
cursor: undefined,
142142
},
143-
apiKey,
144143
developerPlatformClient,
145144
resubscribeCallback,
146145
storeName,
147146
organizationId,
148147
abortSignal,
148+
logsDir,
149149
}).catch((error) => {
150150
outputDebug(`Unexpected error during polling: ${error}}\n`)
151151
})

packages/app/src/cli/services/app-logs/dev/write-app-logs.test.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {writeAppLogsToFile} from './write-app-logs.js'
22
import {AppLogData, AppLogPayload, FunctionRunLog} from '../types.js'
33
import {joinPath} from '@shopify/cli-kit/node/path'
4-
import {writeLog} from '@shopify/cli-kit/node/logs'
4+
import {writeFile} from '@shopify/cli-kit/node/fs'
55
import {describe, expect, test, vi, beforeEach} from 'vitest'
66
import camelcaseKeys from 'camelcase-keys'
77
import {formatLocalDate} from '@shopify/cli-kit/common/string'
88

9-
vi.mock('@shopify/cli-kit/node/logs')
9+
vi.mock('@shopify/cli-kit/node/fs')
1010

1111
const APP_LOG: AppLogData = {
1212
shop_id: 1,
@@ -33,8 +33,8 @@ const NEW_APP_LOG: AppLogData = {
3333
}
3434

3535
const FUNCTION_RUN_PAYLOAD = new FunctionRunLog(camelcaseKeys(JSON.parse(APP_LOG.payload)))
36-
const API_KEY = 'apiKey'
3736
const STORE_NAME = 'storeName'
37+
const TEST_LOGS_DIR = '/test/logs/dir'
3838

3939
describe('writeAppLogsToFile', () => {
4040
let stdout: any
@@ -45,17 +45,16 @@ describe('writeAppLogsToFile', () => {
4545

4646
test('calls writeLog with the FunctionRunLog payload type', async () => {
4747
// Given
48-
// determine the fileName and path
48+
// determine the fileName
4949
const fileName = `20240522_150641_827Z_${APP_LOG.source_namespace}_${APP_LOG.source}`
50-
const path = joinPath(API_KEY, fileName)
5150

5251
// When
5352
const returnedPath = await writeAppLogsToFile({
5453
appLog: APP_LOG,
5554
appLogPayload: FUNCTION_RUN_PAYLOAD,
56-
apiKey: API_KEY,
5755
stdout,
5856
storeName: STORE_NAME,
57+
logsDir: TEST_LOGS_DIR,
5958
})
6059

6160
// Then
@@ -75,29 +74,29 @@ describe('writeAppLogsToFile', () => {
7574
}
7675
const expectedLogData = JSON.stringify(expectedSaveData, null, 2)
7776

78-
expect(returnedPath.fullOutputPath.startsWith(path)).toBe(true)
79-
expect(writeLog).toHaveBeenCalledWith(expect.stringContaining(path), expectedLogData)
77+
expect(writeFile).toHaveBeenCalledWith(expect.stringContaining(fileName), expectedLogData)
78+
expect(returnedPath.fullOutputPath).toEqual(expect.stringContaining(joinPath(TEST_LOGS_DIR, fileName)))
79+
expect(returnedPath.fullOutputPath).toEqual(expect.stringContaining(TEST_LOGS_DIR))
8080
})
8181

8282
test('calls writeLog with strings when no matching payload type', async () => {
8383
// Given
84-
// determine the fileName and path
85-
const fileName = `20240522_150641_827Z_${APP_LOG.source_namespace}_${APP_LOG.source}`
86-
const path = joinPath(API_KEY, fileName)
84+
// determine the fileName
85+
const fileName = `20240522_150641_827Z_${NEW_APP_LOG.source_namespace}_${NEW_APP_LOG.source}`
8786

8887
// When
8988
const returnedPath = await writeAppLogsToFile({
9089
appLog: NEW_APP_LOG,
9190
appLogPayload: JSON.parse(NEW_APP_LOG.payload),
92-
apiKey: API_KEY,
9391
stdout,
9492
storeName: STORE_NAME,
93+
logsDir: TEST_LOGS_DIR,
9594
})
9695

9796
// Then
98-
expect(returnedPath.fullOutputPath.startsWith(path)).toBe(true)
99-
expect(writeLog).toHaveBeenCalledWith(
100-
expect.stringContaining(path),
97+
expect(returnedPath.fullOutputPath).toEqual(expect.stringContaining(joinPath(TEST_LOGS_DIR, fileName)))
98+
expect(writeFile).toHaveBeenCalledWith(
99+
expect.stringContaining(fileName),
101100
expectedLogDataFromAppEvent(NEW_APP_LOG, JSON.parse(NEW_APP_LOG.payload)),
102101
)
103102
})

packages/app/src/cli/services/app-logs/dev/write-app-logs.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {AppLogData} from '../types.js'
22
import {toFormattedAppLogJson} from '../utils.js'
33
import {joinPath} from '@shopify/cli-kit/node/path'
4-
import {writeLog, getLogsDir} from '@shopify/cli-kit/node/logs'
4+
import {mkdir, writeFile} from '@shopify/cli-kit/node/fs'
55
import {randomUUID} from '@shopify/cli-kit/node/crypto'
66
import {Writable} from 'stream'
77

@@ -13,26 +13,27 @@ interface AppLogFile {
1313
export const writeAppLogsToFile = async ({
1414
appLog,
1515
appLogPayload,
16-
apiKey,
1716
stdout,
1817
storeName,
18+
logsDir,
1919
}: {
2020
appLog: AppLogData
2121
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2222
appLogPayload: any
23-
apiKey: string
2423
stdout: Writable
2524
storeName: string
25+
logsDir: string
2626
}): Promise<AppLogFile> => {
2727
const identifier = randomUUID().substring(0, 6)
28-
2928
const formattedTimestamp = formatTimestampToFilename(appLog.log_timestamp)
3029
const fileName = `${formattedTimestamp}_${appLog.source_namespace}_${appLog.source}_${identifier}.json`
31-
const path = joinPath(apiKey, fileName)
32-
const fullOutputPath = joinPath(getLogsDir(), path)
30+
const logContent = toFormattedAppLogJson({appLog, appLogPayload, prettyPrint: true, storeName})
31+
const fullOutputPath = joinPath(logsDir, fileName)
3332

3433
try {
35-
await writeLog(path, toFormattedAppLogJson({appLog, appLogPayload, prettyPrint: true, storeName}))
34+
await mkdir(logsDir)
35+
await writeFile(fullOutputPath, logContent)
36+
3637
return {
3738
fullOutputPath,
3839
identifier,

packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import {DeveloperPlatformClient} from '../../../utilities/developer-platform-cli
99
import * as appLogsUtils from '../../app-logs/utils.js'
1010
import {AppEventWatcher} from '../app-events/app-event-watcher.js'
1111
import {AbortSignal} from '@shopify/cli-kit/node/abort'
12-
import {createLogsDir} from '@shopify/cli-kit/node/logs'
12+
import {mkdir} from '@shopify/cli-kit/node/fs'
1313
import {outputDebug} from '@shopify/cli-kit/node/output'
1414
import {describe, expect, vi, Mock, beforeEach, test} from 'vitest'
1515

16-
vi.mock('@shopify/cli-kit/node/logs')
16+
vi.mock('@shopify/cli-kit/node/fs')
1717
vi.mock('@shopify/cli-kit/node/output')
1818
vi.mock('../../app-logs/dev/poll-app-logs.js')
1919

@@ -102,7 +102,7 @@ describe('app-logs-polling', () => {
102102

103103
developerPlatformClient = testDeveloperPlatformClient({subscribeToAppLogs})
104104

105-
vi.mocked(createLogsDir).mockResolvedValue()
105+
vi.mocked(mkdir).mockResolvedValue()
106106
vi.mocked(pollAppLogs).mockResolvedValue()
107107
vi.spyOn(appLogsUtils, 'subscribeToAppLogs').mockResolvedValue(JWT_TOKEN)
108108
})
@@ -144,12 +144,12 @@ describe('app-logs-polling', () => {
144144
'organizationId',
145145
stdout,
146146
)
147-
expect(createLogsDir).toHaveBeenCalledWith(API_KEY)
147+
expect(mkdir).toHaveBeenCalledWith(localApp.getLogsDir())
148148
expect(pollAppLogs).toHaveBeenCalledOnce()
149149
expect(vi.mocked(pollAppLogs).mock.calls[0]?.[0]).toMatchObject({
150150
stdout,
151151
appLogsFetchInput: {jwtToken: JWT_TOKEN},
152-
apiKey: API_KEY,
152+
logsDir: localApp.getLogsDir(),
153153
})
154154

155155
const eventCallback = appWatcher.onEvent.mock.calls[0][0]

packages/app/src/cli/services/dev/processes/app-logs-polling.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {subscribeToAppLogs} from '../../app-logs/utils.js'
66
import {AppLinkedInterface} from '../../../models/app/app.js'
77
import {AppEventWatcher, AppEvent} from '../app-events/app-event-watcher.js'
88

9-
import {createLogsDir} from '@shopify/cli-kit/node/logs'
9+
import {mkdir} from '@shopify/cli-kit/node/fs'
1010
import {outputDebug} from '@shopify/cli-kit/node/output'
1111

1212
function hasFunctionExtensions(app: AppLinkedInterface): boolean {
@@ -66,7 +66,7 @@ export async function setupAppLogsPollingProcess({
6666

6767
export const subscribeAndStartPolling: DevProcessFunction<SubscribeAndStartPollingOptions> = async (
6868
{stdout, stderr: _stderr, abortSignal: _abortSignal},
69-
{developerPlatformClient, appLogsSubscribeVariables, storeName, organizationId, appWatcher, localApp: _localApp},
69+
{developerPlatformClient, appLogsSubscribeVariables, storeName, organizationId, appWatcher, localApp},
7070
) => {
7171
async function startPolling(abortSignal?: AbortSignal) {
7272
const jwtToken = await subscribeToAppLogs(
@@ -76,20 +76,20 @@ export const subscribeAndStartPolling: DevProcessFunction<SubscribeAndStartPolli
7676
stdout,
7777
)
7878

79-
const apiKey = appLogsSubscribeVariables.apiKey
80-
await createLogsDir(apiKey)
79+
const logsDir = localApp.getLogsDir()
80+
await mkdir(logsDir)
8181

8282
await pollAppLogs({
8383
stdout,
8484
appLogsFetchInput: {jwtToken},
85-
apiKey,
8685
resubscribeCallback: () => {
8786
return subscribeToAppLogs(developerPlatformClient, appLogsSubscribeVariables, organizationId)
8887
},
8988
developerPlatformClient,
9089
storeName,
9190
organizationId,
9291
abortSignal,
92+
logsDir,
9393
})
9494
}
9595

0 commit comments

Comments
 (0)