[實作筆記] Stripe 串接

Agenda

  • Stripe 簡介
  • 註冊
  • 付款

Stripe 簡介

Stripe 是一家提供讓個人或公司在網際網路上接受付款服務的科技公司。
Stripe 提供在網上接受付款所需的技術、避免信用卡詐騙技術及銀行基礎設施

– 引述自 Wiki

Our mission is to increase
the GDP of the internet

– 引述自 Stripe 官網

業務範圍大多為歐美,亞洲方面支援香港、新加坡與日本等國…

註冊

只需要透過信箱即可註冊
註冊後需要到信箱收取確認信以開通帳戶,
如果真的要在線上使用需要提供公司相關的資訊,
但以開發者而言,此時的 Stripe 已經提供一組測試 api 供你使用,
隨後即可以登入後台操作。
請在 Dashboard 的左邊側欄>開發者>API 密鑰,取得 Secret key
在後面呼叫 API 中都會使用這組 Secret Key 請特別留意。

付款

這裡只介紹 Stripe 信用卡的付款方法,
並根據 Stripe 文件整理一些資訊給大家。

如下圖,這是一個標準的 Stripe 結帳流程,
stripe overview

主要的兩個步驟在 Create SourceCreate Charge
這裡會透過呼叫 Stripe API 以完成付款流程。

下面會介紹幾種信用卡的付款方式, 僅供參考,實際作業請以最新的 Stripe 文件為準。
過程中如有呼叫 API 都會用 curl 帶過,
Secret Key 一律以 sk 表示, Public Key 以 pk 表示
Stripe 有提供多種語言的範例或是提供 SDK 或 Libary, 請親自去看它們的文件囉。

使用 Checkout Session

Step 1. 建立 Session

1
2
3
4
5
6
7
8
9
10
11
curl https://api.stripe.com/v1/checkout/sessions \
-u sk_test_dAa6L6BL4gZDuscgJcl3an8K00aJL2yIaW: \
-d payment_method_types[]=card \
-d line_items[][name]=T-shirt \
-d line_items[][description]="Comfortable cotton t-shirt" \
-d line_items[][images][]="https://example.com/t-shirt.png" \
-d line_items[][amount]=500 \
-d line_items[][currency]=hkd \
-d line_items[][quantity]=1 \
-d success_url="https://example.com/success" \
-d cancel_url="https://example.com/cancel"

Step 2. 建立 CheckOut 頁面

Step 1 會取得一組 session_id ,請填入頁面中的{session_id}
pk 請填入 public key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script src="https://js.stripe.com/v3/"></script>
<script>
var stripe = Stripe("pk");
stripe
.redirectToCheckout({
// Make the id field from the Checkout Session creation API response
// available to this file, so you can provide it as parameter here
// instead of the {{CHECKOUT_SESSION_ID}} placeholder.
sessionId: "{session_id}",
})
.then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
});
</script>

Step 3. 載入頁面

調整你的付款流程,引導消費者到 Step 2. 的頁面,
會自動轉導到 Stripe 的標準頁,並且出現填寫信用卡的資訊,
消費者需要手動輸入卡號後,確認付款。
如果確認會引導至 Step 1 的 success_url
消費者取消的話會引導至 Step 1 的 cancel_url

說明

這是標準的第三方串接步驟,可以發現在 Step 3 的時候,
消費者會被帶離你原本的站台到 Stripe 的付款頁面,
這樣的好處是你不需要經手敏感的資料,像是信用卡卡號,
但是有時候,轉導到外部頁面會讓消費者不安進而中斷結帳,
那我們可以參考其它的作法。

使用 Source

一般來說,Source 是 Stripe 最常用的付款方式,
但在歐洲相關規定調整後,信用卡不再建議使用這個 API。
可以參考官方文件的說明

Card Payments with Sources
Use Sources to accept card payments from around the world.

Use of this API is no longer recommended. We recommend adopting the Payment Intents API.
This new integration lets you benefit from Dynamic 3D Secure and helps you prepare for
Strong Customer Authentication regulation in Europe.

不過理論上您的客戶中沒有歐洲人的話,還是可以呼叫這個 API ,
作法如下:

Step 1. Create Source 並指定 Type 為 Card

1
2
3
4
5
6
7
8
9
curl https://api.stripe.com/v1/sources
-u sk
-d type=card
-d currency=hkd
-d owner[email]="[email protected]"
-d card[number]={{cardNumber}}
-d card[exp_month]=12
-d card[exp_year]=2020
-d card[cvc]=123

Step 2. Charge With Source

Step 1 可以取得 source id,利用 source id 呼叫 charge API 付款

1
2
3
4
5
6
7
8
curl https://api.stripe.com/v1/charges
-u sk
-d source={{source id}}
-d amount=411
-d currency=hkd
-d description="Charge for [email protected]"
-d metadata[a]="b"
-d metadata[b]=123

使用 Token

不過 Sorce 信用卡在官方文件上不再被建議使用,
我們可以看看另一個類似的方法 Token

Step 1. Create Token 並傳入卡號

1
2
3
4
5
6
curl https://api.stripe.com/v1/tokens
-u sk
-d card[number]={{cardNumber}}
-d card[exp_month]=12
-d card[exp_year]=2020
-d card[cvc]=123

Step 2. Charge With Token

Step 1 可以取得 token id,利用 source id 呼叫 charge API 付款

1
2
3
4
5
6
7
8
curl https://api.stripe.com/v1/charges
-u sk
-d source={{token id}}
-d amount=412
-d currency=hkd
-d description="Charge for [email protected]"
-d metadata[a]="c"
-d metadata[b]=663

使用 Payment Intent

終於來到 Payment Intent 了,
實際上這是目前 Stripe 最推薦的信用卡支付方式,
呼叫的作法也很類似於 Source 與 Token,
沒什麼特別考量的話,建議使用這個付款方式。

Step 1. Create Payment Method 並傳入卡號

1
2
3
4
5
6
7
8
curl https://api.stripe.com/v1/payment_methods
-u sk
-X POST
-d type=card
-d card[number]={{cardNumber}}
-d card[exp_month]=12
-d card[exp_year]=2020
-d card[cvc]=123

Step 2. Create Payment Intent With Payment Method

Step 1 可以取得 payment method id,
利用 payment method id 呼叫 payment intents API 付款

1
2
3
4
5
6
7
8
curl https://api.stripe.com/v1/payment_intents
-u sk
-d payment_method={{payment method id}}
-d amount=555
-d currency=hkd
-d confirmation_method=manual
-d confirm=true

小結

想快速成立訂單請用 Session 的作法,
想要避免轉換率下降,請使用 Payment Intents。

參考

(fin)

[實作筆記] AWS DeepRacer

概念

Reinforcement Learning

強化學習(Reinforcement Learning , 簡稱 RL),
就好像在訓練寵物坐下或握手一樣,
透過獎勵(Reward)特定的行為(Action)來達到訓練的目標,

DeepRacer 的角色

Agent

在比賽中是真實的縮小迷你車(1:18),車上帶有鏡頭可以拍攝路況,

在訓練中則是使用模擬器,可以模擬真實世界開車時會遇到的變數,

註:這只是模型車,真實世界的車子會更複雜,變數會更多更不可測。

  • Environment
  • Action
  • Reward
  • State

實作

  1. 建立資源

  2. 預設的 獎勵方程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def reward_function(params):
'''
Example of rewarding the agent to follow center line
'''

# Read input parameters
track_width = params['track_width']
distance_from_center = params['distance_from_center']

# Calculate 3 markers that are at varying distances away from the center line
marker_1 = 0.1 * track_width
marker_2 = 0.25 * track_width
marker_3 = 0.5 * track_width

# Give higher reward if the car is closer to center line and vice versa
if distance_from_center <= marker_1:
reward = 1.0
elif distance_from_center <= marker_2:
reward = 0.5
elif distance_from_center <= marker_3:
reward = 0.1
else:
reward = 1e-3 # likely crashed/ close to off track

return float(reward)

除錯

錯誤訊息

Error in IAM role creation
Please try again after deleting the following roles: AWSDeepRacerServiceRole,
AWSDeepRacerSageMakerAccessRole, AWSDeepRacerRoboMakerAccessRole,
AWSDeepRacerLambdaAccessRole, AWSDeepRacerCloudFormationAccessRole.

這是 IAM Roles 已經存在, 通常是你已經建立過了這些 Roles , 只要刪除了就可以了

使用資源與服務

  • DeepRacer
  • Reinforcement learning > Reinforcement learning
  • CloudWatch > Log Groups
  • IAM
    • AWSDeepRacerServiceRole
    • AWSDeepRacerSageMakerAccessRole
    • AWSDeepRacerRoboMakerAccessRole
    • AWSDeepRacerLambdaAccessRole
    • AWSDeepRacerCloudFormationAccessRole.

如何清除

  • DeepRacer > 按下 Reset

參考

(fin)

[實作筆記] 讓 SonarQube 檢查你的代碼

前情提要

  1. SonarQube 是一個開源的代碼品質(Quality)管理系統
  2. 我目前的公司 N 社是自架 SonarQube Server 再與 CI 結合
  3. 能夠透過工具讓代碼品質提昇,我訂定的目標如下
    • 免費
    • 能夠與 CI 結合,持續檢查代碼品質
    • 與 Side Project 結合

應該要知道的事

  1. 掃瞄環境為 Windows
  2. 掃瞄專案為 .Net Core 2.2 版
  3. 使用 PowerShell 執行 Command
  4. 也許不需要知道

記錄

  1. 申請SonarCloud帳號,我直接使用 Github

    • 需要允許 SonarCloud 存取 Github 的專案 Repo
    • 建立一組 Token, 用來作身份驗証,可以重複使用請勿外流
    • 如果要刪除 Token 請至 My Account > Security 找到並 Revoke
      建立 token
      建立 token2
    • 下載 SonarQube 執行檔,請選擇你的語言
      執行命令
  2. 執行掃瞄前的準備作業

    • 設定 Path (實務上我沒有設定)
    • 切換到專案目錄底下
  3. 啟動掃瞄,以 .Net Core 為例

1
> dotnet "{path of sonar scanner}\SonarScanner.MSBuild.dll" begin /k:"{project name}" /o:{group name} /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="{your token}"

輸出結果

1
2
3
4
Using the .NET Core version of the Scanner for MSBuild
Pre-processing started.
中略...
16:40:31.855 Pre-processing succeeded.
  1. 建置專案
1
dotnet build

輸出結果

1
2
3
4
Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
中略...
Build succeeded.
  1. 上傳結果
1
> dotnet "{path of sonar scanner}\SonarScanner.MSBuild.dll" end /d:sonar.login="{your token}"

輸出結果

1
2
3
4
5
6
7
8
9
10
11
12
SonarScanner for MSBuild 4.6.1
Using the .NET Core version of the Scanner for MSBuild
Post-processing started.
中略...
INFO: Analysis total time: 21.446 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 2:27.021s
INFO: Final Memory: 24M/72M
INFO: ------------------------------------------------------------------------
The SonarQube Scanner has finished

最後到 SonarCloud 的網站上就可以看到報告結果,
下一步就是將這整段流程結合 CI ,官網推薦是使用 Travis CI,
也有相同的文件與資源,我會試試看或是使用 Jenkins,
如果有機會能更進一步,我想結合 OpenShift ,
讓部署的過程中結合代碼品質檢查。

結果上傳

參考

(fin)

[實作筆記] Unit Testing With TypeScript

前情提要

  • 難得有機會寫前端的東西
  • 其實只有寫 JavaScript
  • 我要用 TypeScript 寫
  • 合理的軟體工序 TDD
  • 所以我要寫測試
  • node 版本 v8.11.1
  • npm 版本 5.6.0
  • TypeScript 版本 3.3.3333

Context & User Stories

來自前端的需求,在一個日曆工具要加入對可選日期判斷的邏輯。
原始需求如下,g、h、i、j 可以透過修改日曆元件選項調整,
而細微的日期與時間判斷需要撰寫新的方法作判斷。

原始需求

a. 每週一過中午 12 點不能選週二及以前的日期
b. 每週二過中午 12 點不能選週三及以前的日期
c. 每週三過中午 12 點不能選週四及以前的日期
d. 每週四過中午 12 點不能選週五及以前的日期
e. 每週五過中午 12 點不能選隔週一及以前的日期
f. 每週日都不能選
g. 90 天以後的日期不能選
h. 需指導我們如何讓特定日期不能選,以因應遇到國定假日的狀況
i. 預設為選擇最近一個可以使用的日期
j. 改成中文

測試環境準備

使用 Mocha 與 Chai

1
npm i -D chai mocha nyc ts-node typescript

安裝 TypeScript

安裝至專案

1
>$ npm i -D typescript ts-node

安裝至全域

1
>$ npm i -g typescript

建立專案 tsconfig.json

1
>$ tsc --init

安裝 MochaJs

1
npm i -D mocha @types/mocha

安裝 Chai

1
npm i -D Chai @types/chai

設定測試

package.json

1
2
3
"scripts":{
"test": "mocha -r ts-node/register tests/**/*.test.ts",
}

寫測試

1
2
3
4
5
6
7
8
9
import { expect } from 'chai';
import Calculator from '../src/calculate';

describe('calculate', function() {
it('add', function() {
let result = Calculator.Sum(5, 2);
expect(result).equal(7);
});
});

實作代碼

1
2
3
4
export default class calculator {
static Sum(a: number, b: number): number {

}

執行測試

1
npm t

修正代碼

1
2
3
4
5
export default class calculator {
static Sum(a: number, b: number): number {
let c = a + b;
eturn c;
}

測試結果

1
2
3
4
5
calculate
√ add


1 passing (61ms)

測試覆蓋率

// TODO

安裝 nyc

1
npm i -D nyc

設定 scripts

1
2
3
"scripts":{
"testCover": "nyc -r lcov -e .ts -x \"*.test.ts\" mocha -r ts-node/register tests/**/*.test.ts && nyc report",
}

執行

1
> npm run testCover

執行結果

1
2
3
4
5
6
7
8
9
10
11
12
略過測試部份
------------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
------------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 98.63 | 100 | |
src | 100 | 100 | 87.5 | 100 | |
Calculator.ts | 100 | 100 | 100 | 100 | |
beforeShowDay.ts | 100 | 100 | 80 | 100 | |
tests | 100 | 100 | 100 | 100 | |
beforeShowDay.test.ts | 100 | 100 | 100 | 100 | |
calculator.test.ts | 100 | 100 | 100 | 100 | |
------------------------|----------|----------|----------|----------|-------------------|

完整 Test Cases

今天是 2019/3/30 號星期六 12:05
√ 日曆上 2019/4/02 星期二 出貨 可以選
√ 日曆上 2019/4/02 星期二 設定為國定假日, 出貨 不可以選

今天是 2019/3/24 號星期日 12:05
√ 日曆上 2019/3/25 星期一 出貨 不可以選
√ 日曆上 2019/3/26 星期二 出貨 可以選

今天是 2019/3/23 號星期六 12:05
√ 日曆上 2019/3/25 星期一 出貨 不可以選
√ 日曆上 2019/3/26 星期二 出貨 可以選

今天是 2019/3/22 號星期五 23:05
√ 日曆上 2019/3/25 星期一 出貨 不可以選
√ 日曆上 2019/3/26 星期二 出貨 可以選

今天是 2019/3/21 號星期四 01:30
√ 日曆上 2019/3/21 星期四 出貨 不可以選
√ 日曆上 2019/3/22 星期五 出貨 可以選

今天是 2019/3/22 號星期五 23:05
√ 日曆上 2019/3/25 星期一 出貨 不可以選
√ 日曆上 2019/3/26 星期二 出貨 可以選

今天是 2019/3/19 號星期二 23:05
√ 日曆上 2019/3/21 星期四 出貨 可以選

今天是 2019/3/22 號星期五 12:59
√ 日曆上 2019/3/22 星期五 出貨 不能選,因為現在時間超過 12 點
√ 日曆上 2019/3/25 星期一 出貨 不能選,因為現在時間超過 12 點
√ 日曆上 2019/3/26 星期二 出貨 可以選
√ 日曆上 2019/4/1 星期一 出貨 可以選
√ 日曆上 2019/3/23 星期六 出貨 不能選,因為現在時間超過 12 點

今天是 2019/3/21 號星期四 23:59
√ 日曆上 2019/3/21 星期四 出貨 不能選,因為現在時間超過 12 點

今天是 2019/3/18 號星期一 12:00
√ 日曆上 2019/3/18 星期一 出貨 不能選,因為現在是 12 點
√ 日曆上 2019/3/19 星期二 出貨 不能選,因為現在是 12 點
√ 日曆上 2019/3/20 星期三 出貨 可以選
√ 日曆上 2019/3/21 星期四 出貨 可以選

今天是 2019/3/18 號星期一 10:00
√ 日曆上 2019/3/24 星期日 出貨;不能選,因為週日都不能選
√ 日曆上 2019/3/18 星期一 出貨 不可以選,因為當天不能選
√ 日曆上 2019/3/19 星期二 出貨 可以選
√ 日曆上 2019/3/20 星期三 出貨 可以選

心得

能堅持「工序」是專業人士的表現,
我相信這是一種實務上能保持品質與速度的作法。
特別是越大規模越複雜的專案,
避免掉進焦油坑的方法就是一開始就別踩下去。

寫測試案例也是一種技能,在這次的 Case 中,
E2E 測試是相對困難的,受限於時間與日期,
單元測試可以控制時間反而成了絕妙的工具。

我一開始寫的測試並不優良,涵蓋的情境不夠(註:這裡並非指程式碼的函蓋率),
但是與 PO 反覆確認之後,調試出的情境終於滿足了需求,
看來我還要增進一下寫測試案例的能力。

最後,下次再試試用 Jest 寫寫看。

參考

(fin)

[實作筆記] ASP.NET 專案部署地雷-消失的靜態檔

應該知道的事

  • 這個是 Debug 的筆記
  • 用的是 .Net Framework 4.6 不是 .Net Core
  • 對你可能沒有幫助

問題

方案裡面有三個 Web 專案 Web1 、Web2 、Web3,
因開發某功能需要加入一個文字靜態檔 Iamfile.txt
在部署的時候卻無法將檔案部署至網站根目錄。

誤解

對檔案按右鍵 > 屬性 > 複製的輸出目錄 > 下拉選取一律複製。

複製的輸出目錄

很可惜,這個設定的調整會讓這個檔案在建置的時候輸出到指定資料夾中( Ex: \bin ),
這個操作會影響 csproj 如下:

1
2
3
<None Include="Iamfile.txt">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

正解

注意 Tag 的名稱為 None,需要調整為 Content
目前不確定如何用 IDE 操作。

1
<Content Include="Iamfile.txt" />

補充

找到你的 MSBuild 版本

1
cd C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin 

執行建置

1
λ MSBuild.exe D:\Projects\IsASolution.sln /p:Configuration=QA;DeployOnBuild=true;PublishProfile=Mall.QA.pubxml;MvcBuildViews=false;AutoVersion=True

執行後輸出的位置需要看你的部署檔 *.pubxml 如下範例
可以在 D:\Archives\QA\IsASolution 找到我的輸出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<!--
此檔案是由您 Web 專案的發行/封裝處理程序所使用。您可以編輯此 MSBuild 檔案,
以自訂此處理程序的行為。若要深入了解,請造訪 http://go.microsoft.com/fwlink/?LinkID=208121。
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<LastUsedBuildConfiguration>QA</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<ExcludeApp_Data>False</ExcludeApp_Data>
<publishUrl>D:\Archives\QA\IsASolution</publishUrl>
<DeleteExistingFiles>True</DeleteExistingFiles>
<PrecompileBeforePublish>True</PrecompileBeforePublish>
<EnableUpdateable>True</EnableUpdateable>
<DebugSymbols>False</DebugSymbols>
<WDPMergeOption>DonotMerge</WDPMergeOption>
</PropertyGroup>
</Project

(fin)

[實作筆記] ASP.Net Core Logger

要知道的事

  • 這是個人的學習記錄
  • 可能對你沒幫助
  • 網路上資訊很多
  • 希望對你有幫助

概念

Asp.Net Core 的 Life Cycle 由 Program.csmain 方法開始(是的,就如同其它一般的程式),
WebHostBuilder 中的 ConfigureLogging 可以提供彈性讓你設定屬於你的 LoggerProvider,
不論是微軟提供、知名的第三方套件或是你手工自已刻一個,大致你的程式碼會如下

1
2
3
4
5
6
7
8
9
10
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(logging=>
{
logging.ClearProviders();
logging.AddEventLog();
logging.AddFile("D:\\Temp\\Log.txt");
logging.AddConsole();
})
.UseStartup<Startup>()

而在 Controller 或其它 Module 間,你只要透過建構子注入 logger 實體就可以實現 log 的功能

1
2
3
4
public HomeController(ILogger<HomeController> logger)
{
this._logger = logger;
}

預設的行為

如果你沒有呼叫 ConfigureLogging 預設的行為如下述.

The default project template calls CreateDefaultBuilder, which adds the following logging providers:

  • Console
  • Debug
  • EventSource (starting in ASP.NET Core 2.2)
1
2
3
4
5
6
7
8
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();

Find the Logs

Console

範例說明:在建構子中注入 ILogger 實體,運行網站後連到 Home\Index 頁面,並觀察 Console

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class HomeController : Controller
{
private readonly ILogger _logger;

/// <summary>
/// Initializes a new instance of the <see cref="HomeController" /> class.
/// </summary>
public HomeController(ILogger<HomeController> logger)
{
this._logger = logger;
}

public IActionResult Index()
{
this._logger.Log(LogLevel.Information,"HomeController Information");
this._logger.Log(LogLevel.Critical,"HomeController Critical");
this._logger.Log(LogLevel.Debug,"HomeController Debug");
this._logger.Log(LogLevel.Error,"HomeController Error");
this._logger.Log(LogLevel.None,"HomeController None");
this._logger.Log(LogLevel.Trace,"HomeController Trace");
this._logger.Log(LogLevel.Warning,"HomeController Warning");
return View();
}
}

結果如下,可以發現 LogLevel.NoneLogLevel.TraceLogLevel.Warning 並未出現在 Console 資訊當中

1
2
3
4
5
6
7
8
info: Marsen.NetCore.Site.Controllers.HomeController[0]
HomeController Information
crit: Marsen.NetCore.Site.Controllers.HomeController[0]
HomeController Critical
dbug: Marsen.NetCore.Site.Controllers.HomeController[0]
HomeController Debug
fail: Marsen.NetCore.Site.Controllers.HomeController[0]
HomeController Error

LogLevel說明了 None 的意義就是不記錄任何訊息,

Enum Level Description
Trace 0 Logs that contain the most detailed messages. These messages may contain sensitive application data. These messages are disabled by default and should never be enabled in a production environment.
Debug 1 Logs that are used for interactive investigation during development. These logs should primarily contain information useful for debugging and have no long-term value.
Information 2 Logs that track the general flow of the application. These logs should have long-term value.
Warning 3 Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application execution to stop.
Error 4 Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a failure in the current activity, not an application-wide failure.
Critical 5 Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires immediate attention.
None 6 Not used for writing log messages. Specifies that a logging category should not write any messages.

Log 的作用範圍會受 appsettings.json 影響,
另外要注意 appsettings.json 的載入順序.

1
2
3
4
5
6
"Logging": {
"LogLevel": {
"Default": "Trace"
"System": "Information"
"Microsoft": "Information"
}

Debug

如同 Console 的行為一般,可以在 Visual Studio 的輸出(Output)>偵錯(Debug)視窗中,查詢到記錄。

Console

EventSource

如同官方文件所說,我下載了 PerfView
如下圖作了設定,
PerfView
不過我並沒有取得記錄,
PerfView Log

錯誤訊息如下
EventSource Microsoft-Extensions-Logging: Object reference not set to an instance of an object
暫時不打算深追查,
ETW 可以記錄的 Memory 、Disc IO 、CPU 等資訊,
其實與我想要的應用程式記錄有所差異,稍稍記錄一下以後也許用得到。
如果有人能留言給我一些方向,也是非常歡迎。

自訂 Filelog 與 EventLog

調整一下程式

1
2
3
4
5
6
7
8
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(logging=>
{
logging.AddEventLog();
logging.AddFile("D:\\Temp\\Log.txt");
})
.UseStartup<Startup>()

這裡我使用 Microsoft.Extensions.Logging.EventLog 處理 EventLog 可以在 Event View 中看見記錄;
而 file log 我使用 Serilog.Extensions.Logging.File , 特別要注意以下兩點

  • Nuget 使用的版本為 2.0.0 以上版本,目前仍然不是穩定版本
  • AddFile 傳入的是記錄檔的完整 Path 而非目錄

自訂 Elmah

Elmah 在 Net 算是一個蠻方便的工具,有提供簡易介面、可以選擇用 File 或是 Database 方式作 Logging,
更重要是小弟我用了 4 年,順手就研究一下。

設定相當簡單, 在 Startup.csConfigureServices 加入

1
2
3
4
5
6
services.AddElmah<XmlFileErrorLog>(options =>
{
//options.CheckPermissionAction = context => context.User.Identity.IsAuthenticated;
//options.Path = @"elmah";
options.LogPath = "D:\\Temp\\elmah";
})

Configure 加入

1
app.UseElmah();

要注意是使用 XmlFileErrorLog 時,要設定的 options 是 LogPath 而非 Path
其實使用 File 只能說是開發環境的暫時處置,真正的 Prodction 應該將 Log 放到專門的 Database 或是 Cloud Service 之中,
在這裡可以看見 Elmah 的行為與 Net Core 的行為並不一致,Log 與錯誤記錄本來就不該混為一談。
我想我要調整一下我的想法了,不過關於 Log 暫時就到此為止。

參考

(fin)

[閱讀筆記] 重構---改善既有程式的設計,第一章

為什麼

知道自已不知道

上次在公司內部開始進行 Coding Dojo,在 FizzBuzz 的 Kata 嚐到了甜頭
但是下一道題目「Bowling」,卻卡住了。
我們有測試,也通過測試,但是卻寸步難行,
在重構上我們非常的弱,這裡的重構不是指一次性的全面翻掉,
而是逐步的、可靠的前進,
我想習得這樣的技能,因為現場的代碼腐敗的更加嚴重,
如果連 Kata 產生的代碼都不能優化,
那想對產品指手劃腳只不過是說幹話。

閱讀經典:重構—改善即有的程式設計

重構

這是一本來自 Martin Fowler 的經典書籍,新版已經出了,而且是以 JavaScript 作為範例語言。
不過我手頭上借到的是以 Java 作為範例的板本。

CH1 重構,第一個案例

第一個問題就是我找不到書中說的「線上範例」,
即使找到我也沒有 Java 的開發環境,所以心一橫就開始了改寫成 C# 的計劃
這部份比我想像中的簡單很多,兩個語言是相同類似的,
第 1 章,第一個案例

接下來只要照著書上一步一步作就會…覺得越來越沒 fu …
為什麼 ???

其實 Martin 大叔在書中有提到「為即將修改的程式建立可靠的測試…畢竟是人,可能會犯錯。所以我需要可靠的測試」。
沒 fu 的原因就是我沒加測試,即使重構了,我也不知道好壞。
沒有反饋是很糟糕的一件事。

CH1 重構,第一個案例,加上測試

那麼要怎麼加測試呢 ?
書上的案例我分析了一下,其實重構的目標只是一個單純的方法
會針對不同的情境回傳不同的字串。

簡單的說,我只要讓測試覆蓋這個方法就可以開始重構了,
我選擇 dotCover來檢驗我的覆蓋率。
選擇的原因很簡單,因為我買了 ReSharper
其中就包含這個好用的工具。
試著投資一些金錢在工具上的回報是相當值得的。
如果有更好用更便宜的工具也請介紹給我。

OS:課金真的能解決很多人生問題啊(茶)…

最後的結果,我開了一個分支包含了 100%的測試覆蓋率,
這樣就可以開開心心重構了,相信我有測試真得很有感覺。

重構的技法請自行看書,我只稍微作個記錄,有興趣可以 fork 回去玩。

  • Extract Method
  • Move Method
  • Replace Temp with Query
  • Replace Type Code with State/Strategy Pattern
  • Replace Conditional with Polymorphism
  • Self Encapsulate Field

在重構的過程中我儘可能讓步驟小(Baby Step),看我的 commit 歷程即可知道,但是最好可以自已作作看。
另外有一些心法,也稍作個記錄

  • 把一坨爛 Code 抽到獨立的方法之中
  • 如果一個類別方法並沒有使用到該類別的資訊
    • 考慮職責,是不是要讓它搬家
    • 提醒自已這是個壞味道
  • 拆分職責時,有個方法相依兩個不同的類別的資訊,那應該將方法放在哪裡呢?(這裡花了點時間理解)
    • 將方法放在未來可能變化較大的類別之中
    • 相依的資訊作為方法參數傳進來
    • 這樣未來有異動就被縮限在這個類別裡面。
  • 暫存變數常常會帶來問題(壞味道)
    • 儘可能的把它消除
    • 要考慮效能的問題(書上後面會說。)
  • 保持小步調、頻繁測試
    • 使用中繼方法可以縮小重構步調(特別是對 public 的方法)
    • 讓新的 return 值插在舊的 return 之前
    • 測試 ok 就可以刪掉舊 code (有時刪不掉也還是可以運作的)
    • 善用變異測試
  • UML 可以幫助對程式重構前後的理解
  • Java 與 C# 對繼承的處理是不同的

後記

第一章的範例完成後的結果大致如下
100%!!!
很帥氣的 100%啊,這樣的 code 測試覆蓋率 100 % 全綠燈,
而且完成了重構,根本是現場不可能出現的完全體程式碼!!!
代碼的部份我會放在最後的參考區塊。

我有沒有可能讓它更好?或是找出他的缺陷呢?
下一步,我有沒有可能讓它更好?或是找出他的缺陷呢?

這個時候我想起了變異測試
還沒有實作過,來玩看看好了。

首先要選擇測試工具,這裡使用了Stryker Mutator
但是注意只能用在 .Net Core 的版本
照著官網安裝完成後執行

1
>dotnet stryker

跑下去竟然真的找到有存活的變異
存活的變異
這兩個變異存活的原因是類似的,

1
2
3
4
double result = 2;
if (daysRented > 2)
result += (daysRented - 2) * 1.5;
return result;

變異點發生在 daysRented > 2 的判斷式之中,
現有的測試在變異發生(daysRented >= 2)時,無法提出警訊,也就是測試上的不足。
不過依現有的邏輯,不論是進入 if 進行了加 0 運算,或是直接回傳 result,
都是等價的(回傳 2 ),目前還沒有想法怎麼強化我的測試,
希望有先進願意不嗇指點,實務上跟本沒在跑變異測試。

後記 2

回歸一下我們當初 Kata 的目的:

  • 學習 Pair ,並透過 Pair 彼此學習
  • 學習 TDD ,並透過 TDD 學習重構
  • 學習 Vim,並提昇開發速度

事情沒有那麼簡單,比如說學習 Vim 的過程中,
我們的目的是增進開發速度,但是一開始反而會變慢,
一定要刻意的練習才能習得,
你必須擁有以下的能力。

  • 打字速度,網路上很多資源,我是使用Ratatype作練習
    • 能盲打
    • 指法要正確(特別在特殊符號)
    • 快速切換中英(建議加入英文輸入法用 win + space 之切換過去)
  • 英文能力。命名是開發很重要的一課,英文不好看不懂寫得差,命名自然不會好。
  • 熟悉工具,特別是你的 IDE 與外掛
    • Visual Studio
    • Resharper
    • OzCode
    • more ..
  • Vim
    • Vim Basic 基本功(v、c、i、s、j、k、g、h、l….)
    • VimRc 要學會配置自已的 VimRC,這裡不僅要刻意練習,還要刻意試錯找到自已最順的模式

彼此學習方面需要相當的軟技能,
溝通、尊重、謙虛…;這些一生的功課我就不贅言了。
Pair Programming 一半是 Pair 一半是 Programming;
而在進入 Programming 之前請搞懂你要作什麼

同樣的在 TDD 的過程之中,我們沒有事先理好需求,
沒有想好作好需求分析,隨便選了測試案例就開始進行,
如果好好分析,是可以歸納出其中的邏輯,
甚至是理出 test case 的順序。

重要的是過程,但是我們太在乎結果,以致程式快速的腐敗。
甚至到了難以修改的狀態,僅管有測試保護,卻無法重構。

這是很好的一課,特別在這裡記錄一下。

參考

(fin)

[實作筆記]使用 Windows PowerShell 批次上傳 AWS S3

目標

相關服務要雲端化,要將站台、資料等…遷移至 AWS,
這個案例需要將大量在 File Server 上的檔案(報表/單據/報告書等…)上傳至 S3
因為檔案數量相當的大,所以開發一個簡單的指令來執行。

Step 1 下載並安裝適用於 Windows PowerShell 的 AWS 工具

只需要安裝必要的程式

設定 aws config

打開 terminal 執行以下語法

1
> aws configure

依指示設定 Access key IDSecret access key
這個資料需要具備一定的權限才能取得,如果權限不足請向你的 AWS 服務管理員申請。

撰寫 PowerShell 與執行

1
2
3
4
5
6
7
8
9
10
11
12
$bucketName = "********************-your_bucket_name"
$path = "Your\s3\path\"

Get-ChildItem -Filter *.pdf |
ForEach-Object -Begin{$i=0} {
$i++;
$key = $path+$_ ;
## 進度顯示
Write-Host $key "($i/1000)" -ForegroundColor Green ;
## 上傳 S3
Write-S3Object -BucketName $bucketName -File $_.FullName -Key $key ;
}

(fin)

[讀書筆記] 重構 CH2 — 為何重構?

前情提要

《重構》第二章直接問了一個最基本的問題:為什麼要重構?

這章不談手法,談的是動機與時機——什麼情況下該動,什麼情況下不該動。

什麼是重構

使用一系列手法,在不影響軟體行為的前提下,重新整理程式碼,
目的是讓程式更容易被理解與修改。

兩個重點:

  1. 不改行為——重構不是重寫,外部行為要維持一致
  2. 不加功能——重構時只整理,不順手加新東西;加功能時只加,不順手整理

這兩件事混在一起做,是最常見的陷阱。

重構的五個好處

1. 改善軟體設計
沒有最好的設計,只有最適合當下的設計。以前適合的,現在不一定還適合。重構讓設計跟得上現實。

2. 讓程式更容易被理解
程式碼是寫給人看的,機器執行只是順便。好的命名比任何註解都有效——認真學英文、認真命名。

3. 幫你找到臭蟲
整理程式碼的過程中,藏在角落的問題往往自己跑出來。

4. 提高開發速度
短期看重構花時間,長期看沒有品質就沒有速度。
百米的速率一定比馬拉松快,但走得好才能走得遠——包含你的職涯。

5. 讓設計不腐爛
不重構的程式碼,每次修改都在舊的混亂上疊新的混亂,最終無法動彈。

什麼時候該重構

三次法則
第一次做,就做。第二次做類似的,忍著。第三次再遇到,就該重構了。

實務上這條法則常常失效:

  • 系統夠大時,很多人都以為自己只是「第二次」
  • 第三次剛好很忙,下次再說
  • 第四次發現前人都這樣做,那就照舊吧
  • 於是臭味就這樣永久留了下來,還順便創造了就業機會

添加功能時
新功能難加,通常是舊程式碼的問題。先整理,再加功能——但記住,不要同時做。

修補錯誤時
先加測試,確認錯誤可以被重現,再重構,再修。順序很重要。

Code Review 時
Code Review 是發現壞味道最自然的時機。Pair Programming 也是一種 Code Review。
只在 merge 時才做 Code Review,時機太晚、氣氛尷尬,改動成本也高。

小結

重構不是為了重構而重構,是為了讓程式碼繼續能被修改、能被理解。
時機比手法更重要——在對的時間動刀,比會一百種手法更有價值。

參考

  • Martin Fowler《重構:改善既有程式的設計》

(fin)

Please enable JavaScript to view the LikeCoin. :P