added different logging strategies

This commit is contained in:
Umed Khudoiberdiev 2017-07-26 21:25:46 +05:00
parent eeacff886f
commit 3ecd594b81
46 changed files with 637 additions and 279 deletions

View File

@ -62,6 +62,8 @@ Also now all
* create and update dates in entities now use date with fractional seconds.
* `@PrimaryGeneratedColumn` decorator now accept generation strategy as first argument (default is `increment`), instead of column type.
Column type must be passed in options object, e.g. `@PrimaryGeneratedColumn({ type: "bigint"})`
* Logger interface has changed. Custom logger supply mechanism has changed
* Now `logging` options in connection options is simple "true", or "all", or list of logging modes can be supplied
### DEPRECATIONS
@ -83,6 +85,7 @@ Also now all
* sql queries are highlighted in console
* added `@Generated` decorator. It can accept `strategy` option with values `increment` and `uuid`.
Default is `increment`. It always generates value for column, except when column defined as `nullable` and user sets `null` value in to column.
* added logging of log-running requests
### OTHER API CHANGES

View File

@ -1,4 +1,3 @@
# Extra features
* Using custom logger
* Using in browsers

155
docs/logging.md Normal file
View File

@ -0,0 +1,155 @@
# Logging
* Enabling logging
* Logging options
* Log long-running sql queries
* Changing default logger
* Using custom logger
## Enabling logging
You can enable all queries logging by simply setting `logging: true` in your connection options:
```typescript
{
name: "mysql",
type: "mysql",
host: "localhost",
port: 3306,
username: "test",
password: "test",
database: "test",
...
logging: true
}
```
This configuration will enable all executing queries logging + errors of failed queries.
## Logging options
You can enable different types of logging in connection options:
```typescript
{
host: "localhost",
...
logging: ["query", "error"]
}
```
If you want to enable only logging of failed queries then only enable `error` in configuration:
```typescript
{
host: "localhost",
...
logging: ["error"]
}
```
There are few other options you can use:
* `query` - enables all query logging
* `error` - enables failed query error logging
* `schema` - enables schema build process logging
* `warn` - enables internal orm warning messages logging
* `info` - enables internal orm informative messages logging
* `log` - enables internal orm log messages logging
You can specify as many of logging options as needed.
If you want to enable all logging you can simply specify `logging: "all"`:
```typescript
{
host: "localhost",
...
logging: "all"
}
```
## Log long-running queries
If you have performance issues you can log queries that execute too much time
by setting `maxQueryExecutionTime` option in connection options:
```typescript
{
host: "localhost",
...
maxQueryExecutionTime: 1000
});
```
This code will log all queries which run more then `1 second`.
## Changing default logger
There are several loggers TypeORM ships with 3 different types of loggers:
* `advanced-console` - this is default logger which logs all messages into console using color
and sql syntax highlighting (using [chalk]() package)
* `simple-console` - this is simple console logger which is exactly the same as advanced, but it does not use any color highlighting.
This logger can be used if you have problems / or don't like colorized logs
* `file` - this logger writes all logs into `ormlogs.log` file in the root folder of your project (near `package.json` and `ormconfig.json`)
You can enable any of them in connection options this way:
```typescript
{
host: "localhost",
...
logger: "simple-console",
logging: true
}
```
## Using custom logger
You can create your own logger class by implementing `Logger` interface and
specifying your custom logger in connection options:
```typescript
import {Logger} from "typeorm";
export class MyCustomLogger implements Logger {
// implement all methods from logger class
}
```
And specify it in connection options:
```typescript
import {createConnection} from "typeorm";
import {MyCustomLogger} from "./logger/MyCustomLogger";
createConnection({
name: "mysql",
type: "mysql",
host: "localhost",
port: 3306,
username: "test",
password: "test",
database: "test",
logger: MyCustomLogger()
});
```
If you defined your connection options in `ormconfig` file,
then you can use it and override following way:
```typescript
import {createConnection, getConnectionOptions} from "typeorm";
import {MyCustomLogger} from "./logger/MyCustomLogger";
// getConnectionOptions will read options from your ormconfig file
// and return it in connectionOptions object
// then you can simply append additional properties to it
getConnectionOptions().then(connectionOptions => {
return createConnection(Object.assign(connectionOptions, {
logger: new MyCustomLogger()
}))
});
```

View File

@ -4,4 +4,24 @@
* Specify custom column name
* Specify custom foreign column name
* Specify custom many-to-many junction table name
* Creating your own `NamingStrategy`
* Creating your own `NamingStrategy`
## Creating your own `NamingStrategy`
If you defined your connection options in `ormconfig` file,
then you can simply use it and override it following way:
```typescript
import {createConnection, getConnectionOptions} from "typeorm";
import {MyNamingStrategy} from "./logger/MyNamingStrategy";
// getConnectionOptions will read options from your ormconfig file
// and return it in connectionOptions object
// then you can simply append additional properties to it
getConnectionOptions().then(connectionOptions => {
return createConnection(Object.assign(connectionOptions, {
namingStrategy: new MyNamingStrategy()
}))
});
```

View File

@ -1,6 +1,7 @@
import "reflect-metadata";
import {ConnectionOptions, createConnection} from "../../src/index";
import {Post} from "./entity/Post";
import {QueryFailedError} from "../../src/error/QueryFailedError";
const options: ConnectionOptions = {
// type: "oracle",
@ -22,35 +23,41 @@ const options: ConnectionOptions = {
// username: "test",
// password: "test",
// database: "test",
"type": "mssql",
"host": "192.168.1.6",
"username": "sa",
"password": "admin12345",
"database": "test",
// "type": "mssql",
// "host": "192.168.1.6",
// "username": "sa",
// "password": "admin12345",
// "database": "test",
// port: 1521,
// type: "sqlite",
// database: "temp/sqlitedb.db",
logging: {
logQueries: true,
logFailedQueryError: false,
logSchemaCreation: true
},
type: "sqlite",
database: "temp/sqlitedb.db",
logging: ["query", "error"],
// logging: ["error", "schema", "query"],
autoSchemaSync: true,
entities: [Post]
};
createConnection(options).then(connection => {
createConnection(options).then(async connection => {
let post = new Post();
post.text = "Hello how are you?";
post.title = "hello";
post.likesCount = 100;
try {
await connection.query("CREATE DATABASE 'aaaa' AND DIE");
let postRepository = connection.getRepository(Post);
} catch (err) {
console.log("-------------------------------");
console.log("ERRROR: ", err instanceof QueryFailedError);
console.log(err);
}
postRepository
.save(post)
.then(post => console.log("Post has been saved: ", post))
.catch(error => console.log("Cannot save. Error: ", error));
// let post = new Post();
// post.text = "Hello how are you?";
// post.title = "hello";
// post.likesCount = 100;
// let postRepository = connection.getRepository(Post);
// postRepository
// .save(post)
// .then(post => console.log("Post has been saved: ", post))
// .catch(error => console.log("Cannot save. Error: ", error));
}, error => console.log("Cannot connect: ", error));

View File

@ -9,9 +9,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [EverythingEntity]
};

View File

@ -12,10 +12,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [__dirname + "/entity/*"]
};

View File

@ -10,12 +10,7 @@ const options: ConnectionOptions = {
username: "test",
password: "test",
database: "test",
logging: {
logQueries: true,
logFailedQueryError: true,
logOnlyFailedQueries: true,
logSchemaCreation: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, BasePost]
};

View File

@ -9,10 +9,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post]
};

View File

@ -11,10 +11,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, Author, Category]
};

View File

@ -12,10 +12,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, Author, Category, PostMetadata]
};

View File

@ -15,9 +15,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
// logQueries: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, PostDetails, PostCategory, PostMetadata, PostImage, PostInformation, PostAuthor]
};

View File

@ -11,10 +11,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, Author, Category]
};

View File

@ -11,10 +11,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, Author, Category]
};

View File

@ -9,10 +9,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Category]
};

View File

@ -11,10 +11,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, Author, Category]
};

View File

@ -10,10 +10,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, Author]
};

View File

@ -11,10 +11,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, Question, Counters]
};

View File

@ -9,10 +9,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post]
};

View File

@ -12,11 +12,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
// logQueries: true,
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [
Person,

View File

@ -12,11 +12,7 @@ const options: ConnectionOptions = {
username: "root",
password: "admin",
database: "test",
logging: {
// logQueries: true,
logOnlyFailedQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [
Person,

View File

@ -21,10 +21,7 @@ const options: ConnectionOptions = {
port: 1521,
sid: "xe.oracle.docker",
autoSchemaSync: true,
logging: {
logQueries: true,
logFailedQueryError: true
},
logging: ["query", "error"],
entities: [Post, PostDetails, PostCategory, PostMetadata, PostImage, PostInformation, PostAuthor]
};

View File

@ -6,10 +6,7 @@ import {Category} from "./entity/Category";
const options: ConnectionOptions = {
type: "sqlite",
database: "temp/sqlitedb.db",
logging: {
logQueries: true,
logSchemaCreation: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [Post, Category]
};

View File

@ -9,11 +9,7 @@ const options: ConnectionOptions = {
database: "temp/sqlitedb.db",
tablesPrefix: "samples_", // pay attention on this prefix
autoSchemaSync: true,
logging: {
logQueries: true,
logSchemaCreation: true,
logFailedQueryError: true
},
logging: ["query", "error"],
entities: [Post, Author, Category],
};

View File

@ -11,9 +11,7 @@ const options: ConnectionOptions = {
password: "admin",
database: "test",
autoSchemaSync: true,
logging: {
logQueries: true,
},
logging: ["query", "error"],
entities: [Post, Author],
};
@ -45,9 +43,7 @@ createConnection(options).then(async connection => {
username: "test",
password: "test",
database: "test",
logging: {
logQueries: true
},
logging: ["query", "error"],
entities: [
Post,
Author

View File

@ -15,9 +15,7 @@ const options: ConnectionOptions = {
password: "admin",
database: "test",
autoSchemaSync: true,
logging: {
logQueries: true,
},
logging: ["query", "error"],
entities: [Post, Author, User],
};

View File

@ -6,10 +6,7 @@ const options: ConnectionOptions = {
type: "mongodb",
host: "localhost",
database: "test",
logging: {
logQueries: true,
logSchemaCreation: true
},
logging: ["query", "error"],
// autoSchemaSync: true,
entities: [Post]
};

View File

@ -9,10 +9,7 @@ const options: ConnectionOptions = {
username: "sa",
password: "admin12345",
database: "test",
logging: {
logFailedQueryError: true,
// logQueries: true
},
logging: ["query", "error"],
autoSchemaSync: true,
entities: [__dirname + "/entity/*"]
};

View File

@ -3,6 +3,7 @@ import {EntitySchema} from "../entity-schema/EntitySchema";
import {LoggerOptions} from "../logger/LoggerOptions";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {DatabaseType} from "../driver/types/DatabaseType";
import {Logger} from "../logger/Logger";
/**
* BaseConnectionOptions is set of connection options shared by all database types.
@ -81,6 +82,16 @@ export interface BaseConnectionOptions {
*/
readonly logging?: LoggerOptions;
/**
* Logger instance used to log queries and events in the ORM.
*/
readonly logger?: "advanced-console"|"simple-console"|"file"|Logger;
/**
* Maximum number of milliseconds query should be executed before logger log a warning.
*/
readonly maxQueryExecutionTime?: number;
/**
* Drops the schema each time connection is being established.
* Be careful with this option and don't use this in production - otherwise you'll loose all production data.

View File

@ -21,12 +21,12 @@ import {EntityMetadataValidator} from "../metadata-builder/EntityMetadataValidat
import {ConnectionOptions} from "./ConnectionOptions";
import {QueryRunnerProviderAlreadyReleasedError} from "../error/QueryRunnerProviderAlreadyReleasedError";
import {EntityManagerFactory} from "../entity-manager/EntityManagerFactory";
import {LoggerFactory} from "../logger/LoggerFactory";
import {DriverFactory} from "../driver/DriverFactory";
import {ConnectionMetadataBuilder} from "./ConnectionMetadataBuilder";
import {QueryRunner} from "../query-runner/QueryRunner";
import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder";
import {SqliteDriver} from "../driver/sqlite/SqliteDriver";
import {LoggerFactory} from "../logger/LoggerFactory";
/**
* Connection is a single database ORM connection to a specific database.
@ -96,7 +96,7 @@ export class Connection {
constructor(options: ConnectionOptions) {
this.name = options.name || "default";
this.options = options;
this.logger = new LoggerFactory().create(this.options.logging || {});
this.logger = new LoggerFactory().create(this.options.logger, this.options.logging);
this.driver = new DriverFactory().create(this);
this.manager = new EntityManagerFactory().create(this);
this.namingStrategy = options.namingStrategy || new DefaultNamingStrategy();

View File

@ -32,11 +32,7 @@ export class ConnectionOptionsEnvReader {
migrations: PlatformTools.getEnvVariable("TYPEORM_MIGRATIONS") ? PlatformTools.getEnvVariable("TYPEORM_MIGRATIONS").split(",") : [],
subscribers: PlatformTools.getEnvVariable("TYPEORM_SUBSCRIBERS") ? PlatformTools.getEnvVariable("TYPEORM_SUBSCRIBERS").split(",") : [],
entitySchemas: PlatformTools.getEnvVariable("TYPEORM_ENTITY_SCHEMAS") ? PlatformTools.getEnvVariable("TYPEORM_ENTITY_SCHEMAS").split(",") : [],
logging: {
logQueries: OrmUtils.toBoolean(PlatformTools.getEnvVariable("TYPEORM_LOGGING_QUERIES")),
logFailedQueryError: OrmUtils.toBoolean(PlatformTools.getEnvVariable("TYPEORM_LOGGING_FAILED_QUERIES")),
logOnlyFailedQueries: OrmUtils.toBoolean(PlatformTools.getEnvVariable("TYPEORM_LOGGING_ONLY_FAILED_QUERIES")),
},
logging: PlatformTools.getEnvVariable("TYPEORM_LOGGING"),
cli: {
entitiesDir: PlatformTools.getEnvVariable("TYPEORM_ENTITIES_DIR"),
migrationsDir: PlatformTools.getEnvVariable("TYPEORM_MIGRATIONS_DIR"),

View File

@ -32,11 +32,7 @@ export class ConnectionOptionsXmlReader {
entities: connection.entities ? connection.entities[0].entity : [],
subscribers: connection.subscribers ? connection.subscribers[0].entity : [],
entitySchemas: connection.entitySchemas ? connection.entitySchemas[0].entity : [],
logging: {
logQueries: connection.logging && connection.logging[0].logQueries ? connection.logging[0].logQueries[0] : undefined,
logFailedQueryError: connection.logging && connection.logging[0].logFailedQueryError ? connection.logging[0].logFailedQueryError[0] : undefined,
logOnlyFailedQueries: connection.logging && connection.logging[0].logOnlyFailedQueries ? connection.logging[0].logOnlyFailedQueries[0] : undefined,
}
logging: connection.logging[0] ? connection.logging[0].split(",") : undefined,
};
});
}

View File

@ -14,6 +14,7 @@ import {ReadStream} from "fs";
import {EntityManager} from "../../entity-manager/EntityManager";
import {OrmUtils} from "../../util/OrmUtils";
import {InsertResult} from "../InsertResult";
import {QueryFailedError} from "../../error/QueryFailedError";
/**
* Runs queries on a single mysql database connection.
@ -166,9 +167,8 @@ export class MysqlQueryRunner implements QueryRunner {
this.driver.connection.logger.logQuery(query, parameters, this);
databaseConnection.query(query, parameters, (err: any, result: any) => {
if (err) {
this.driver.connection.logger.logFailedQuery(query, parameters, this);
this.driver.connection.logger.logQueryError(err, this);
return fail(err);
this.driver.connection.logger.logQueryError(err, query, parameters, this);
return fail(new QueryFailedError(query, parameters, err));
}
ok(result);

View File

@ -13,6 +13,7 @@ import {OracleDriver} from "./OracleDriver";
import {Connection} from "../../connection/Connection";
import {ReadStream} from "fs";
import {EntityManager} from "../../entity-manager/EntityManager";
import {QueryFailedError} from "../../error/QueryFailedError";
/**
* Runs queries on a single oracle database connection.
@ -177,9 +178,8 @@ export class OracleQueryRunner implements QueryRunner {
this.driver.connection.logger.logQuery(query, parameters, this);
const handler = (err: any, result: any) => {
if (err) {
this.driver.connection.logger.logFailedQuery(query, parameters, this);
this.driver.connection.logger.logQueryError(err, this);
return fail(err);
this.driver.connection.logger.logQueryError(err, query, parameters, this);
return fail(new QueryFailedError(query, parameters, err));
}
ok(result.rows || result.outBinds);

View File

@ -14,6 +14,7 @@ import {ReadStream} from "fs";
import {EntityManager} from "../../entity-manager/EntityManager";
import {OrmUtils} from "../../util/OrmUtils";
import {InsertResult} from "../InsertResult";
import {QueryFailedError} from "../../error/QueryFailedError";
/**
* Runs queries on a single postgres database connection.
@ -113,8 +114,7 @@ export class PostgresQueryRunner implements QueryRunner {
connection.query(`SET search_path TO '${this.schemaName}', 'public';`, (err: any) => {
if (err) {
this.driver.connection.logger.logFailedQuery(`SET search_path TO '${this.schemaName}', 'public';`, [], this);
this.driver.connection.logger.logQueryError(err, this);
this.driver.connection.logger.logQueryError(err, `SET search_path TO '${this.schemaName}', 'public';`, [], this);
fail(err);
} else {
ok(connection);
@ -191,9 +191,8 @@ export class PostgresQueryRunner implements QueryRunner {
this.driver.connection.logger.logQuery(query, parameters, this);
databaseConnection.query(query, parameters, (err: any, result: any) => {
if (err) {
this.driver.connection.logger.logFailedQuery(query, parameters, this);
this.driver.connection.logger.logQueryError(err, this);
fail(err);
this.driver.connection.logger.logQueryError(err, query, parameters, this);
fail(new QueryFailedError(query, parameters, err));
} else {
ok(result.rows);
}

View File

@ -16,6 +16,7 @@ import {ReadStream} from "fs";
import {EntityManager} from "../../entity-manager/EntityManager";
import {OrmUtils} from "../../util/OrmUtils";
import {InsertResult} from "../InsertResult";
import {QueryFailedError} from "../../error/QueryFailedError";
/**
* Runs queries on a single sqlite database connection.
@ -145,9 +146,8 @@ export class SqliteQueryRunner implements QueryRunner {
this.driver.connection.logger.logQuery(query, parameters, this);
databaseConnection.all(query, parameters, (err: any, result: any) => {
if (err) {
this.driver.connection.logger.logFailedQuery(query, parameters, this);
this.driver.connection.logger.logQueryError(err, this);
fail(err);
this.driver.connection.logger.logQueryError(err, query, parameters, this);
fail(new QueryFailedError(query, parameters, err));
} else {
ok(result);
}
@ -180,8 +180,7 @@ export class SqliteQueryRunner implements QueryRunner {
const databaseConnection = await this.connect();
databaseConnection.run(sql, parameters, function (err: any): void {
if (err) {
__this.driver.connection.logger.logFailedQuery(sql, parameters, this);
__this.driver.connection.logger.logQueryError(err, this);
__this.driver.connection.logger.logQueryError(err, sql, parameters, this);
fail(err);
} else {
const generatedMap = generatedColumns.reduce((map, generatedColumn) => {

View File

@ -14,6 +14,7 @@ import {ReadStream} from "fs";
import {MssqlParameter} from "./MssqlParameter";
import {OrmUtils} from "../../util/OrmUtils";
import {EntityManager} from "../../entity-manager/EntityManager";
import {QueryFailedError} from "../../error/QueryFailedError";
/**
* Runs queries on a single mysql database connection.
@ -288,10 +289,9 @@ export class SqlServerQueryRunner implements QueryRunner {
let promiseIndex = this.queryResponsibilityChain.indexOf(promise);
let waitingPromiseIndex = this.queryResponsibilityChain.indexOf(waitingPromise);
if (err) {
this.driver.connection.logger.logFailedQuery(query, parameters, this);
this.driver.connection.logger.logQueryError((err.originalError && err.originalError.info) ? err.originalError.info.message : err, this);
this.driver.connection.logger.logQueryError(err, query, parameters, this);
resolveChain();
return fail(err);
return fail(new QueryFailedError(query, parameters, err));
}
ok(result.recordset);
@ -346,8 +346,7 @@ export class SqlServerQueryRunner implements QueryRunner {
let promiseIndex = this.queryResponsibilityChain.indexOf(promise);
let waitingPromiseIndex = this.queryResponsibilityChain.indexOf(waitingPromise);
if (err) {
this.driver.connection.logger.logFailedQuery(query, parameters, this);
this.driver.connection.logger.logQueryError((err.originalError && err.originalError.info) ? err.originalError.info.message : err, this);
this.driver.connection.logger.logQueryError(err, query, parameters, this);
resolveChain();
return fail(err);
}
@ -378,7 +377,7 @@ export class SqlServerQueryRunner implements QueryRunner {
const generatedColumnSql = generatedColumns.length > 0 ? ` OUTPUT ${generatedColumnNames}` : "";
const sql = columns.length > 0
? `INSERT INTO "${tableName}"(${columns}) ${generatedColumnSql} VALUES (${values})`
: `INSERT INTO "${tableName}" ${generatedColumnSql} DEFAULT VALUES `;
: `INSERT INTO "${tableName}" DEFAULT VALUES `;
const parameters = this.driver.parametrizeMap(tableName, keyValues);
const parametersArray = Object.keys(parameters).map(key => parameters[key]);

View File

@ -14,6 +14,7 @@ import {ReadStream} from "fs";
import {EntityManager} from "../../entity-manager/EntityManager";
import {OrmUtils} from "../../util/OrmUtils";
import {InsertResult} from "../InsertResult";
import {QueryFailedError} from "../../error/QueryFailedError";
/**
* Declare a global function that is only available in browsers that support WebSQL.
@ -195,9 +196,8 @@ export class WebsqlQueryRunner implements QueryRunner {
ok(rows);
}, (tx: any, err: any) => {
this.driver.connection.logger.logFailedQuery(query, parameters, this);
this.driver.connection.logger.logQueryError(err, this);
return fail(err);
this.driver.connection.logger.logQueryError(err, query, parameters, this);
return fail(new QueryFailedError(query, parameters, err));
});
});
});
@ -241,8 +241,7 @@ export class WebsqlQueryRunner implements QueryRunner {
});
}, (tx: any, err: any) => {
this.driver.connection.logger.logFailedQuery(sql, parameters, this);
this.driver.connection.logger.logQueryError(err, this);
this.driver.connection.logger.logQueryError(err, sql, parameters, this);
return fail(err);
});
});

View File

@ -0,0 +1,17 @@
/**
* Thrown when query execution has failed.
*/
export class QueryFailedError extends Error {
constructor(query: string, parameters: any[]|undefined, driverError: any) {
super(driverError.toString().replace(/error: /, "").replace(/Error: /, ""));
Object.setPrototypeOf(this, QueryFailedError.prototype);
Object.assign(this, {
...driverError,
name: "QueryFailedError",
query: query,
parameters: parameters || []
});
}
}

View File

@ -21,6 +21,7 @@ import {PromiseUtils} from "./util/PromiseUtils";
export * from "./container";
export * from "./common/ObjectType";
export * from "./common/ObjectLiteral";
export * from "./error/QueryFailedError";
export * from "./decorator/columns/Column";
export * from "./decorator/columns/CreateDateColumn";
export * from "./decorator/columns/DiscriminatorColumn";
@ -140,6 +141,13 @@ export function getConnectionManager(): ConnectionManager {
return getFromContainer(ConnectionManager);
}
/**
* Reads connection options stored in ormconfig configuration file.
*/
export async function getConnectionOptions(connectionName: string = "default"): Promise<ConnectionOptions> {
return new ConnectionOptionsReader().get(connectionName);
}
/**
* Creates a new connection and registers it in the manager.
*
@ -149,7 +157,7 @@ export function getConnectionManager(): ConnectionManager {
*/
export async function createConnection(options?: ConnectionOptions): Promise<Connection> {
if (!options)
options = await new ConnectionOptionsReader().get("default");
options = await getConnectionOptions();
return getConnectionManager().create(options).connect();
}

View File

@ -0,0 +1,101 @@
import {LoggerOptions} from "./LoggerOptions";
import {PlatformTools} from "../platform/PlatformTools";
import {QueryRunner} from "../query-runner/QueryRunner";
import {Logger} from "./Logger";
const chalk = require("chalk");
/**
* Performs logging of the events in TypeORM.
* This version of logger uses console to log events and does not use syntax highlighting.
*/
export class AdvancedConsoleLogger implements Logger {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private options?: LoggerOptions) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* Logs query and parameters used in it.
*/
logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
if (this.options === "all" || this.options === true || (this.options instanceof Array && this.options.indexOf("query") !== -1)) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
console.log(chalk.gray.underline("executing query") + ": " + PlatformTools.highlightSql(sql));
}
}
/**
* Logs query that is failed.
*/
logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner) {
if (this.options === "all" || this.options === true || (this.options instanceof Array && this.options.indexOf("error") !== -1)) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
console.log(chalk.underline.red(`query failed:`) + " " + chalk.bold(PlatformTools.highlightSql(sql)));
console.log(chalk.underline.red(`error:`), error);
}
}
/**
* Logs query that is slow.
*/
logQuerySlow(time: number, query: string, parameters?: any[], queryRunner?: QueryRunner) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
console.log(chalk.underline.yellow(`query is slow:`) + " " + chalk.bold(PlatformTools.highlightSql(sql)));
console.log(chalk.underline.yellow(`execution time:`), time);
}
/**
* Logs events from the schema build process.
*/
logSchemaBuild(message: string, queryRunner?: QueryRunner) {
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("schema") !== -1)) {
console.log(chalk.underline(message));
}
}
/**
* Perform logging using given logger, or by default to the console.
* Log has its own level and message.
*/
log(level: "log"|"info"|"warn", message: any, queryRunner?: QueryRunner) {
switch (level) {
case "log":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("log") !== -1))
console.log(message);
break;
case "info":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("info") !== -1))
console.info(message);
break;
case "warn":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("warn") !== -1))
console.warn(chalk.yellow(message));
break;
}
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
/**
* Converts parameters to a string.
* Sometimes parameters can have circular objects and therefor we are handle this case too.
*/
protected stringifyParams(parameters: any[]) {
try {
return JSON.stringify(parameters);
} catch (error) { // most probably circular objects in parameters
return parameters;
}
}
}

111
src/logger/FileLogger.ts Normal file
View File

@ -0,0 +1,111 @@
import {LoggerOptions} from "./LoggerOptions";
import {QueryRunner} from "../query-runner/QueryRunner";
import {Logger} from "./Logger";
import {writeFileSync} from "fs";
/**
* Performs logging of the events in TypeORM.
*/
export class FileLogger implements Logger {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private options?: LoggerOptions) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* Logs query and parameters used in it.
*/
logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
if (this.options === "all" || this.options === true || (this.options instanceof Array && this.options.indexOf("query") !== -1)) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
this.write("executing query" + ": " + sql);
}
}
/**
* Logs query that is failed.
*/
logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner) {
if (this.options === "all" || this.options === true || (this.options instanceof Array && this.options.indexOf("error") !== -1)) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
this.write([
`query failed: ` + sql,
`error:`, error
]);
}
}
/**
* Logs query that is slow.
*/
logQuerySlow(time: number, query: string, parameters?: any[], queryRunner?: QueryRunner) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
this.write([
`query is slow: ` + sql,
`execution time: ` + time
]);
}
/**
* Logs events from the schema build process.
*/
logSchemaBuild(message: string, queryRunner?: QueryRunner) {
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("schema") !== -1)) {
this.write(message);
}
}
/**
* Perform logging using given logger, or by default to the console.
* Log has its own level and message.
*/
log(level: "log"|"info"|"warn"|"error", message: any, queryRunner?: QueryRunner) {
switch (level) {
case "log":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("log") !== -1))
this.write(message);
break;
case "info":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("info") !== -1))
this.write(message);
break;
case "warn":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("warn") !== -1))
this.write(message);
break;
}
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
/**
* Writes given strings into the log file.
*/
protected write(strings: string|string[]) {
strings = strings instanceof Array ? strings : [strings];
writeFileSync("ormlogs.log", strings.join("\r\n")); // todo: make async
}
/**
* Converts parameters to a string.
* Sometimes parameters can have circular objects and therefor we are handle this case too.
*/
protected stringifyParams(parameters: any[]) {
try {
return JSON.stringify(parameters);
} catch (error) { // most probably circular objects in parameters
return parameters;
}
}
}

View File

@ -1,113 +1,34 @@
import {LoggerOptions} from "./LoggerOptions";
import {PlatformTools} from "../platform/PlatformTools";
import {QueryRunner} from "../query-runner/QueryRunner";
const chalk = require("chalk");
/**
* Performs logging of the events in TypeORM.
*
* todo: implement logging of too long running queries (there should be option to control max query execution time)
*/
export class Logger {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private options: LoggerOptions) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
export interface Logger {
/**
* Logs query and parameters used in it.
*/
logQuery(query: string, parameters: any[]|undefined, queryRunner?: QueryRunner) {
if (this.options.logQueries ||
PlatformTools.getEnvVariable("LOGGER_CLI_SCHEMA_SYNC")) {
this.log("query", query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : ""), queryRunner);
}
}
logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner): any;
/**
* Logs query that failed.
* Logs query that is failed.
*/
logFailedQuery(query: string, parameters: any[]|undefined, queryRunner?: QueryRunner) {
if (this.options.logQueries ||
this.options.logOnlyFailedQueries ||
PlatformTools.getEnvVariable("LOGGER_CLI_SCHEMA_SYNC"))
this.log("error", `query failed: ${query}${parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : ""}`, queryRunner);
}
logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner): any;
/**
* Logs failed query's error.
* Logs query that is slow.
*/
logQueryError(error: any, queryRunner?: QueryRunner) {
if (this.options.logFailedQueryError ||
PlatformTools.getEnvVariable("LOGGER_CLI_SCHEMA_SYNC"))
this.log("error", "error during executing query:" + error, queryRunner);
}
logQuerySlow(time: number, query: string, parameters?: any[], queryRunner?: QueryRunner): any;
/**
* Logs events from the schema build process.
*/
logSchemaBuild(message: string, queryRunner?: QueryRunner) {
if (this.options.logSchemaCreation ||
PlatformTools.getEnvVariable("LOGGER_CLI_SCHEMA_SYNC"))
this.log("schema-build", message, queryRunner);
}
logSchemaBuild(message: string, queryRunner?: QueryRunner): any;
/**
* Perform logging using given logger, or by default to the console.
* Log has its own level and message.
*/
log(level: "query"|"schema-build"|"log"|"info"|"warn"|"error", message: any, queryRunner?: QueryRunner) {
if (!this.options) return;
if (this.options.logger) {
this.options.logger(level, message, queryRunner);
} else {
switch (level) {
case "schema-build":
console.log(chalk.underline(message));
break;
case "query":
console.log(chalk.gray.underline("executing query") + ": " + PlatformTools.highlightSql(message));
break;
case "log":
console.log(message);
break;
case "info":
console.info(message);
break;
case "warn":
console.warn(message);
break;
case "error":
console.error(message);
break;
}
}
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
/**
* Converts parameters to a string.
* Sometimes parameters can have circular objects and therefor we are handle this case too.
*/
protected stringifyParams(parameters: any[]) {
try {
return JSON.stringify(parameters);
} catch (error) { // most probably circular objects in parameters
return parameters;
}
}
log(level: "log"|"info"|"warn", message: any, queryRunner?: QueryRunner): any;
}

View File

@ -1,5 +1,8 @@
import {LoggerOptions} from "./LoggerOptions";
import {Logger} from "./Logger";
import {LoggerOptions} from "./LoggerOptions";
import {SimpleConsoleLogger} from "./SimpleConsoleLogger";
import {AdvancedConsoleLogger} from "./AdvancedConsoleLogger";
import {FileLogger} from "./FileLogger";
/**
* Helps to create logger instances.
@ -9,8 +12,24 @@ export class LoggerFactory {
/**
* Creates a new logger depend on a given connection's driver.
*/
create(options: LoggerOptions): Logger {
return new Logger(options);
create(logger?: "advanced-console"|"simple-console"|"file"|Logger, options?: LoggerOptions): Logger {
if (logger instanceof Object)
return logger as Logger;
if (logger) {
switch (logger) {
case "simple-console":
return new SimpleConsoleLogger(options);
case "file":
return new FileLogger(options);
case "advanced-console":
return new AdvancedConsoleLogger(options);
}
}
return new AdvancedConsoleLogger(options);
}
}

View File

@ -1,32 +1,4 @@
import {QueryRunner} from "../query-runner/QueryRunner";
/**
* Logging options.
*/
export interface LoggerOptions {
/**
* Some specific logger to be used. By default it is a console.
*/
readonly logger?: (level: string, message: any, queryRunner?: QueryRunner) => void;
/**
* Set to true if you want to log every executed query.
*/
readonly logQueries?: boolean;
/**
* Set to true if you want to log only failed query.
*/
readonly logOnlyFailedQueries?: boolean;
/**
* Set to true if you want to log error of the failed query.
*/
readonly logFailedQueryError?: boolean;
/**
* If set to true then schema creation logs will be logged.
*/
readonly logSchemaCreation?: boolean;
}
export type LoggerOptions = boolean|"all"|("query"|"schema"|"error"|"warn"|"info"|"log")[];

View File

@ -0,0 +1,98 @@
import {LoggerOptions} from "./LoggerOptions";
import {QueryRunner} from "../query-runner/QueryRunner";
import {Logger} from "./Logger";
/**
* Performs logging of the events in TypeORM.
*/
export class SimpleConsoleLogger implements Logger {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private options?: LoggerOptions) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* Logs query and parameters used in it.
*/
logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
if (this.options === "all" || this.options === true || (this.options instanceof Array && this.options.indexOf("query") !== -1)) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
console.log("executing query" + ": " + sql);
}
}
/**
* Logs query that is failed.
*/
logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner) {
if (this.options === "all" || this.options === true || (this.options instanceof Array && this.options.indexOf("error") !== -1)) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
console.log(`query failed: ` + sql);
console.log(`error:`, error);
}
}
/**
* Logs query that is slow.
*/
logQuerySlow(time: number, query: string, parameters?: any[], queryRunner?: QueryRunner) {
const sql = query + (parameters && parameters.length ? " -- PARAMETERS: " + this.stringifyParams(parameters) : "");
console.log(`query is slow: ` + sql);
console.log(`execution time: ` + time);
}
/**
* Logs events from the schema build process.
*/
logSchemaBuild(message: string, queryRunner?: QueryRunner) {
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("schema") !== -1)) {
console.log(message);
}
}
/**
* Perform logging using given logger, or by default to the console.
* Log has its own level and message.
*/
log(level: "log"|"info"|"warn"|"error", message: any, queryRunner?: QueryRunner) {
switch (level) {
case "log":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("log") !== -1))
console.log(message);
break;
case "info":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("info") !== -1))
console.info(message);
break;
case "warn":
if (this.options === "all" || (this.options instanceof Array && this.options.indexOf("warn") !== -1))
console.warn(message);
break;
}
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
/**
* Converts parameters to a string.
* Sometimes parameters can have circular objects and therefor we are handle this case too.
*/
protected stringifyParams(parameters: any[]) {
try {
return JSON.stringify(parameters);
} catch (error) { // most probably circular objects in parameters
return parameters;
}
}
}