-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
feat(logger): add console, cloudWatch providers #12735
feat(logger): add console, cloudWatch providers #12735
Conversation
packages/core/src/Logger/types.ts
Outdated
|
||
// configure the plugin | ||
configure(config?: object): object; | ||
export type LogParams = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use interface
if there is no specific requirements of using type alias.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks updated all type
to interface
except union types and some complex types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some nits and suggestions. The bundle size increase it a little concerning for what it is, could you do an analysis on the bundle and make sure nothing is getting included that shouldn't be?
@@ -23,6 +24,18 @@ export const DefaultAmplify = { | |||
) { | |||
let resolvedResourceConfig: ResourcesConfig; | |||
|
|||
// add console logger provider by default | |||
if (!Amplify.libraryOptions?.Logger) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want to move this logic into the if (libraryOptions)
block below & refactor as necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is slightly updated to align with API review. We attached console
logger by default here at the top.
Amplify.configure()
is called for different scenarios :
- no Auth config is provided
- Auth options are provided
- add default auth providers
if (libraryOptions)
whenAmplify.configure()
is called multiple times
The reason we add it at the top is because we want logger category to be configured for all these scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from the logic at the top, if (libraryOptions)
is updated as well to handle multiple Amplify.configure()
. We make sure "console" provider is always configured. Customer can update console provider config (eg disable) but not remove it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slight updates on this to make sure we don't update the incoming libraryOptions params. Instead we create resolvedLibraryOptions
and update that
...libraryOptions, | ||
Logger: { | ||
...libraryOptions?.Logger, | ||
providers: [consoleProvider, ...customProviders], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens here if a customer also provides a consoleProvider
? We might need to specifically check for that before injecting the default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
answered in this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make the Logger
folder lower case to align with the others (realize it's an existing folder)?
You can do that on OSX using something like:
git mv Logger tmpLogger
git mv tmpLogger logger
Also this file should be lower-case as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep this is an existing folder, makes sense to make it lower case.
updated on PR #12762
renamed this file lower case on this PR
import { Amplify } from '../singleton/Amplify'; | ||
import { LogParams } from './types'; | ||
|
||
export const administrateLogger = (params: LogParams) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit-pick, but don't really care for this name. Maybe something like distributeLog
or sendLogToProviders
🤔 Makes it more clear what it does
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense, renamed to dispatchLogsToProviders
export interface CloudWatchProvider extends LoggerProvider { | ||
initialize: (config: CloudWatchConfig) => void; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this DX align with the API review? Isn't it supposed to be cloudWatchProvder(config)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep the DX would be as per the design, i.e
import {
getConsoleProvider,
} from 'aws-amplify/logger/console';
const consoleProvider = getConsoleProvider({ defaultLogLevel: 'INFO', enable:true })
Added initialize()
methods to both CloudWatchProvider
and ConsoleProvider
as an implementation detail. tagged this as @internal
and @depricated
as well
/** | ||
* @internal | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto about adding some comments around what this is used for, and maybe tag both of these as @deprecated
out of the gate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx makes sense, Added @internal
and @depricated
for LOG_LEVEL getter and setter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added @internal
to all other methods in console and cloudwatch providers
packages/core/src/libraryUtils.ts
Outdated
@@ -118,3 +118,6 @@ export { | |||
SESSION_START_EVENT, | |||
SESSION_STOP_EVENT, | |||
} from './utils/sessionListener'; | |||
|
|||
// logger | |||
export { generateLogger } from './Logger'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be in index.ts
as it's a customer facing util (with re-export from aws-amplify
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this change.
Initially we were planning on exposing logger as a util, the plan is to expose as a category instead.
Add new logger category and it's respective apis covered in #12759
Co-authored-by: Jim Blanchard <jim.l.blanchard@gmail.com>
Co-authored-by: Jim Blanchard <jim.l.blanchard@gmail.com>
Co-authored-by: Jim Blanchard <jim.l.blanchard@gmail.com>
Co-authored-by: Jim Blanchard <jim.l.blanchard@gmail.com>
// console logger is always present | ||
Logger: { | ||
...libraryOptions?.Logger, | ||
console: | ||
libraryOptions?.Logger?.console ?? | ||
Amplify.libraryOptions.Logger?.console, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this block actually needed? The previous block on line 40 is checking "if there's no previously configured logger(s), then set up defaults & include any customer log providers", so wouldn't ...libraryOptions
include it? I might be wrong, just trying to make sure I understand this logic since this function is so critical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous block on line 40 is checking "if there's no previously configured logger(s), then set up defaults & include any customer log providers"
Yep that's correct, so when we do something like this for the first time both console and cloudwatch provider get added.
Amplify.configure(amplifyconfig,
{
Logger: {
additionalProviders: [cloudWatchProvider],
},
}
);
But when we call configure again (same code above) console
provider will get removed since libraryOptions is shallow merged. This isn't on the customer since they didn't configure console provider the first time.
wdyt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, that makes sense
54be2b6
to
64d1135
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, few comments & nits. Also looks like bundle size still requires some tweaking. Nothing super concerning this time as long as we know where it's coming from.
const module = jest.requireActual('../src/logging/ConsoleLogger'); | ||
return { | ||
ConsoleLogger: module.ConsoleLogger, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we even need to mock this if we're just using the full implementation?
|
||
/** | ||
* Sends log event to configured logging providers | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should tag this as @internal
& @private
} | ||
|
||
/** | ||
* Generates a new Logger which can be used to record log events for the specified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specified what?
const client = new CloudWatchLogsClient({ | ||
region, | ||
credentials: session.credentials, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay for now (since we have another task for implementing CW logger), but is this creating a new client for each log statement? I wonder if we should consider caching this & setting up a credential provider fn to keep credentials fresh.
const getConsoleLogFcn = (logLevel: LogLevel) => { | ||
let fcn = console.log.bind(console); | ||
switch (logLevel) { | ||
case 'DEBUG': { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should VERBOSE
map to DEBUG
as well? Not sure how we treat it in the existing logger
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yesss, Great catch @jimblanc
VERBOSE
logLevel should be mapped to console.debug()
|
||
const logLevelIndex = ['VERBOSE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE']; | ||
|
||
export const DEFAULT_LOG_LEVEL: LogLevel = 'WARN'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we default to WARN
? 🤔 I think we use INFO
in some of the function prototypes. We should also reference this value in those prototypes so it's defined in a single place.
@@ -6,6 +6,7 @@ import { | |||
LibraryOptions, | |||
ResourcesConfig, | |||
defaultStorage, | |||
consoleProvider, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we sure we don't need to contextualize this provider further given that it is exported from core root? E.g. consoleLoggingProvider
?
@@ -1,3 +1,10 @@ | |||
jest.mock('../src/logging', () => { | |||
const module = jest.requireActual('../src/logging/ConsoleLogger'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be doing this? Why replace with the actual module in a Hub test instead of properly mocking?
} from '@aws-amplify/core'; | ||
import { ConsoleProvider as ConsoleProviderType } from '@aws-amplify/core/src/logging/providers/console'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Shouldn't this have been ok without the aliasing?
@@ -133,7 +142,7 @@ describe('initSingleton (DefaultAmplify)', () => { | |||
|
|||
expect(mockAmplifySingletonConfigure).toHaveBeenCalledWith( | |||
resourceConfig, | |||
libraryOptions | |||
{ ...libraryLoggingOptions, ...libraryOptions } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this spread order be reversed here and elsewhere below? I think it is reversed in the implementation if I'm not mistaken?
...resolvedLibraryOptions, | ||
Logging: { | ||
...resolvedLibraryOptions?.Logging, | ||
// preserve console logger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QQ: How would console logger have gotten lost here?
}, | ||
}; | ||
|
||
const getConsoleLogFcn = (logLevel: LogLevel) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Imo we should avoid the abbreviation at least in the function naming - I honestly wasn't sure what FCN stood for
export const createLogger = (input: CreateLoggerInput): CreateLoggerOutput => { | ||
const { namespace, category } = input; | ||
return new Logger(namespace, category); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit
export const createLogger = (input: CreateLoggerInput): CreateLoggerOutput => { | |
const { namespace, category } = input; | |
return new Logger(namespace, category); | |
}; | |
export const createLogger = ({ namespace, category }: CreateLoggerInput): CreateLoggerOutput => new Logger(namespace, category); |
const currentLevel = | ||
cloudWatchConfig.loggingConstraints?.defaultLogLevel ?? DEFAULT_LOG_LEVEL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the DEFAULT_LOG_LEVEL needed? Is it because it's possible to call log
before initialize
? Should it be possible to do this?
try { | ||
session = await fetchAuthSession(); | ||
} catch (error) { | ||
return Promise.reject('No credentials'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Probably should refactor as assertion functions?
* @internal | ||
* @deprecated | ||
*/ | ||
set LOG_LEVEL(level: LogLevel) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that the ConsoleLogger still needs to statically have the LOG_LEVEL
but why is it necessary for these providers to too?
Description of changes
Requirements
sample DX
Create cloudWatch service backend and add IAM permissions to auth, unauth roles.
Reference: check 'Add Amplify Logger resources' section
usage
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.