Skip to content

Commit 51b0024

Browse files
authored
Merge pull request #2334 from firebase/next
Release @firebaseextensions/firestore-bigquery-change-tracker
2 parents ebfc016 + a3049a1 commit 51b0024

File tree

7 files changed

+181
-31
lines changed

7 files changed

+181
-31
lines changed

firestore-bigquery-export/firestore-bigquery-change-tracker/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"url": "github.com/firebase/extensions.git",
66
"directory": "firestore-bigquery-export/firestore-bigquery-change-tracker"
77
},
8-
"version": "1.1.39",
8+
"version": "1.1.40",
99
"description": "Core change-tracker library for Cloud Firestore Collection BigQuery Exports",
1010
"main": "./lib/index.js",
1111
"scripts": {

firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/fixtures/changeTracker.ts

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
FirestoreBigQueryEventHistoryTracker,
55
FirestoreDocumentChangeEvent,
66
} from "../..";
7+
import { LogLevel } from "../../logger";
78

89
export const changeTracker = ({
910
datasetId = "",
@@ -22,6 +23,7 @@ export const changeTracker = ({
2223
useIncrementalMaterializedView = false,
2324
maxStaleness = undefined,
2425
refreshIntervalMinutes = undefined,
26+
logLevel = LogLevel.INFO,
2527
}): FirestoreBigQueryEventHistoryTracker => {
2628
return new FirestoreBigQueryEventHistoryTracker({
2729
datasetId,
@@ -40,6 +42,7 @@ export const changeTracker = ({
4042
useIncrementalMaterializedView,
4143
maxStaleness,
4244
refreshIntervalMinutes,
45+
logLevel,
4346
});
4447
};
4548

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { Logger, LogLevel, logger as defaultLogger } from "../logger";
2+
import { logger as funcsLogger } from "firebase-functions";
3+
4+
// Mock firebase-functions logger functions
5+
jest.mock("firebase-functions", () => ({
6+
logger: {
7+
debug: jest.fn(),
8+
info: jest.fn(),
9+
warn: jest.fn(),
10+
error: jest.fn(),
11+
},
12+
}));
13+
14+
describe("Logger", () => {
15+
let log: Logger;
16+
17+
beforeEach(() => {
18+
// Create a new instance before each test
19+
log = new Logger(LogLevel.DEBUG);
20+
jest.clearAllMocks();
21+
});
22+
23+
describe("log level methods", () => {
24+
test("debug should call funcsLogger.debug when level is DEBUG", () => {
25+
log.setLogLevel(LogLevel.DEBUG);
26+
log.debug("debug message");
27+
expect(funcsLogger.debug).toHaveBeenCalledWith("debug message");
28+
});
29+
30+
test("debug should not call funcsLogger.debug when level is INFO", () => {
31+
log.setLogLevel(LogLevel.INFO);
32+
log.debug("debug message");
33+
expect(funcsLogger.debug).not.toHaveBeenCalled();
34+
});
35+
36+
test("info should call funcsLogger.info when level is INFO or lower", () => {
37+
log.setLogLevel(LogLevel.INFO);
38+
log.info("info message");
39+
expect(funcsLogger.info).toHaveBeenCalledWith("info message");
40+
});
41+
42+
test("warn should call funcsLogger.warn when level is WARN or lower", () => {
43+
log.setLogLevel(LogLevel.WARN);
44+
log.warn("warn message");
45+
expect(funcsLogger.warn).toHaveBeenCalledWith("warn message");
46+
});
47+
48+
test("error should call funcsLogger.error when level is ERROR or lower", () => {
49+
log.setLogLevel(LogLevel.ERROR);
50+
log.error("error message");
51+
expect(funcsLogger.error).toHaveBeenCalledWith("error message");
52+
});
53+
54+
test("no logging should occur when log level is SILENT", () => {
55+
log.setLogLevel(LogLevel.SILENT);
56+
log.debug("debug message");
57+
log.info("info message");
58+
log.warn("warn message");
59+
log.error("error message");
60+
expect(funcsLogger.debug).not.toHaveBeenCalled();
61+
expect(funcsLogger.info).not.toHaveBeenCalled();
62+
expect(funcsLogger.warn).not.toHaveBeenCalled();
63+
expect(funcsLogger.error).not.toHaveBeenCalled();
64+
});
65+
});
66+
});

firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { Clustering } from "./clustering";
4444
import { tableRequiresUpdate } from "./checkUpdates";
4545
import { parseErrorMessage, waitForInitialization } from "./utils";
4646
import { initializeLatestView } from "./initializeLatestView";
47+
import { logger, LogLevel } from "../logger";
4748

4849
export { RawChangelogSchema, RawChangelogViewSchema } from "./schema";
4950

@@ -68,6 +69,7 @@ export interface FirestoreBigQueryEventHistoryTrackerConfig {
6869
useIncrementalMaterializedView?: boolean;
6970
maxStaleness?: string;
7071
refreshIntervalMinutes?: number;
72+
logLevel?: LogLevel | string;
7173
}
7274

7375
/**
@@ -94,6 +96,8 @@ export class FirestoreBigQueryEventHistoryTracker
9496
if (!this.config.datasetLocation) {
9597
this.config.datasetLocation = "us";
9698
}
99+
100+
logger.setLogLevel(this.config.logLevel || LogLevel.INFO);
97101
}
98102

99103
async record(events: FirestoreDocumentChangeEvent[]) {

firestore-bigquery-export/firestore-bigquery-change-tracker/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ export {
2424
FirestoreDocumentChangeEvent,
2525
FirestoreEventHistoryTracker,
2626
} from "./tracker";
27+
export { LogLevel, Logger } from "./logger";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2019 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { logger as funcsLogger } from "firebase-functions";
17+
18+
export enum LogLevel {
19+
DEBUG = "debug", // Will log everything
20+
INFO = "info", // Will log info, warnings, and errors
21+
WARN = "warn", // Will log warnings and errors
22+
ERROR = "error", // Will log errors only
23+
SILENT = "silent", // Won't log anything
24+
}
25+
26+
const levels = {
27+
debug: 0,
28+
info: 1,
29+
warn: 2,
30+
error: 3,
31+
silent: 4,
32+
};
33+
34+
export class Logger {
35+
private logLevel: number;
36+
37+
constructor(logLevel: LogLevel | string = LogLevel.INFO) {
38+
this.setLogLevel(logLevel);
39+
}
40+
41+
setLogLevel(logLevel: LogLevel | string): void {
42+
if (typeof logLevel === "string") {
43+
this.logLevel = levels[logLevel as keyof typeof levels] ?? levels.info;
44+
} else {
45+
this.logLevel = levels[logLevel];
46+
}
47+
}
48+
49+
debug(...args: any[]): void {
50+
this.runIfLogLevel(levels.debug, funcsLogger.debug, ...args);
51+
}
52+
53+
info(...args: any[]): void {
54+
this.runIfLogLevel(levels.info, funcsLogger.info, ...args);
55+
}
56+
57+
warn(...args: any[]): void {
58+
this.runIfLogLevel(levels.warn, funcsLogger.warn, ...args);
59+
}
60+
61+
error(...args: any[]): void {
62+
this.runIfLogLevel(levels.error, funcsLogger.error, ...args);
63+
}
64+
65+
log(...args: any[]): void {
66+
this.info(...args);
67+
}
68+
69+
private runIfLogLevel(level: number, func: Function, ...args: any[]): void {
70+
if (this.logLevel <= level) {
71+
func(...args);
72+
}
73+
}
74+
}
75+
76+
export const logger = new Logger();

firestore-bigquery-export/firestore-bigquery-change-tracker/src/logs.ts

+30-30
Original file line numberDiff line numberDiff line change
@@ -16,135 +16,135 @@
1616

1717
import { Table } from "@google-cloud/bigquery";
1818
import { firestore } from "firebase-admin";
19-
import { logger } from "firebase-functions";
19+
import { logger } from "./logger";
2020

2121
export const arrayFieldInvalid = (fieldName: string) => {
2222
logger.warn(`Array field '${fieldName}' does not contain an array, skipping`);
2323
};
2424

2525
export const bigQueryDatasetCreated = (datasetId: string) => {
26-
logger.log(`Created BigQuery dataset: ${datasetId}`);
26+
logger.info(`Created BigQuery dataset: ${datasetId}`);
2727
};
2828

2929
export const bigQueryDatasetCreating = (datasetId: string) => {
30-
logger.log(`Creating BigQuery dataset: ${datasetId}`);
30+
logger.debug(`Creating BigQuery dataset: ${datasetId}`);
3131
};
3232

3333
export const bigQueryDatasetExists = (datasetId: string) => {
34-
logger.log(`BigQuery dataset already exists: ${datasetId}`);
34+
logger.info(`BigQuery dataset already exists: ${datasetId}`);
3535
};
3636

3737
export const bigQueryErrorRecordingDocumentChange = (e: Error) => {
3838
logger.error(`Error recording document changes.`, e);
3939
};
4040

4141
export const bigQueryLatestSnapshotViewQueryCreated = (query: string) => {
42-
logger.log(`BigQuery latest snapshot view query:\n${query}`);
42+
logger.debug(`BigQuery latest snapshot view query:\n${query}`);
4343
};
4444

4545
export const bigQuerySchemaViewCreated = (name: string) => {
46-
logger.log(`BigQuery created schema view ${name}\n`);
46+
logger.debug(`BigQuery created schema view ${name}\n`);
4747
};
4848

4949
export const bigQueryTableAlreadyExists = (
5050
tableName: string,
5151
datasetName: string
5252
) => {
53-
logger.log(
53+
logger.debug(
5454
`BigQuery table with name ${tableName} already ` +
5555
`exists in dataset ${datasetName}!`
5656
);
5757
};
5858

5959
export const bigQueryTableCreated = (tableName: string) => {
60-
logger.log(`Created BigQuery table: ${tableName}`);
60+
logger.info(`Created BigQuery table: ${tableName}`);
6161
};
6262

6363
export const bigQueryTableCreating = (tableName: string) => {
64-
logger.log(`Creating BigQuery table: ${tableName}`);
64+
logger.debug(`Creating BigQuery table: ${tableName}`);
6565
};
6666

6767
export const bigQueryTableUpdated = (tableName: string) => {
68-
logger.log(`Updated existing BigQuery table: ${tableName}`);
68+
logger.info(`Updated existing BigQuery table: ${tableName}`);
6969
};
7070

7171
export const bigQueryTableUpdating = (tableName: string) => {
72-
logger.log(`Updating existing BigQuery table: ${tableName}`);
72+
logger.debug(`Updating existing BigQuery table: ${tableName}`);
7373
};
7474

7575
export const bigQueryTableUpToDate = (tableName: string) => {
76-
logger.log(`BigQuery table: ${tableName} is up to date`);
76+
logger.info(`BigQuery table: ${tableName} is up to date`);
7777
};
7878

7979
export const bigQueryTableValidated = (tableName: string) => {
80-
logger.log(`Validated existing BigQuery table: ${tableName}`);
80+
logger.info(`Validated existing BigQuery table: ${tableName}`);
8181
};
8282

8383
export const bigQueryTableValidating = (tableName: string) => {
84-
logger.log(`Validating existing BigQuery table: ${tableName}`);
84+
logger.debug(`Validating existing BigQuery table: ${tableName}`);
8585
};
8686

8787
export const bigQueryUserDefinedFunctionCreating = (functionName: string) => {
88-
logger.log(`Creating BigQuery user-defined function ${functionName}`);
88+
logger.debug(`Creating BigQuery user-defined function ${functionName}`);
8989
};
9090

9191
export const bigQueryUserDefinedFunctionCreated = (functionName: string) => {
92-
logger.log(`Created BigQuery user-defined function ${functionName}`);
92+
logger.info(`Created BigQuery user-defined function ${functionName}`);
9393
};
9494

9595
export const bigQueryViewCreated = (viewName: string) => {
96-
logger.log(`Created BigQuery view: ${viewName}`);
96+
logger.info(`Created BigQuery view: ${viewName}`);
9797
};
9898

9999
export const bigQueryViewCreating = (viewName: string, query: string) => {
100-
logger.log(`Creating BigQuery view: ${viewName}\nQuery:\n${query}`);
100+
logger.debug(`Creating BigQuery view: ${viewName}\nQuery:\n${query}`);
101101
};
102102

103103
export const bigQueryViewAlreadyExists = (
104104
viewName: string,
105105
datasetName: string
106106
) => {
107-
logger.log(
107+
logger.info(
108108
`View with id ${viewName} already exists in dataset ${datasetName}.`
109109
);
110110
};
111111

112112
export const bigQueryViewUpdated = (viewName: string) => {
113-
logger.log(`Updated existing BigQuery view: ${viewName}`);
113+
logger.info(`Updated existing BigQuery view: ${viewName}`);
114114
};
115115

116116
export const bigQueryViewUpdating = (viewName: string) => {
117-
logger.log(`Updating existing BigQuery view: ${viewName}`);
117+
logger.debug(`Updating existing BigQuery view: ${viewName}`);
118118
};
119119

120120
export const bigQueryViewUpToDate = (viewName: string) => {
121-
logger.log(`BigQuery view: ${viewName} is up to date`);
121+
logger.info(`BigQuery view: ${viewName} is up to date`);
122122
};
123123

124124
export const bigQueryViewValidated = (viewName: string) => {
125-
logger.log(`Validated existing BigQuery view: ${viewName}`);
125+
logger.info(`Validated existing BigQuery view: ${viewName}`);
126126
};
127127

128128
export const bigQueryViewValidating = (viewName: string) => {
129-
logger.log(`Validating existing BigQuery view: ${viewName}`);
129+
logger.debug(`Validating existing BigQuery view: ${viewName}`);
130130
};
131131

132132
export const complete = () => {
133-
logger.log("Completed mod execution");
133+
logger.info("Completed mod execution");
134134
};
135135

136136
export const dataInserted = (rowCount: number) => {
137-
logger.log(`Inserted ${rowCount} row(s) of data into BigQuery`);
137+
logger.debug(`Inserted ${rowCount} row(s) of data into BigQuery`);
138138
};
139139

140140
export const dataInsertRetried = (rowCount: number) => {
141-
logger.log(
141+
logger.debug(
142142
`Retried to insert ${rowCount} row(s) of data into BigQuery (ignoring unknown columns)`
143143
);
144144
};
145145

146146
export const dataInserting = (rowCount: number) => {
147-
logger.log(`Inserting ${rowCount} row(s) of data into BigQuery`);
147+
logger.debug(`Inserting ${rowCount} row(s) of data into BigQuery`);
148148
};
149149

150150
export const dataTypeInvalid = (
@@ -168,11 +168,11 @@ export const timestampMissingValue = (fieldName: string) => {
168168
};
169169

170170
export const addNewColumn = (table: string, field: string) => {
171-
logger.log(`Updated '${table}' table with a '${field}' column`);
171+
logger.info(`Updated '${table}' table with a '${field}' column`);
172172
};
173173

174174
export const addPartitionFieldColumn = (table, field) => {
175-
logger.log(
175+
logger.info(
176176
`Updated '${table}' table with a partition field '${field}' column`
177177
);
178178
};

0 commit comments

Comments
 (0)